ima-claude 2.9.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/LICENSE +21 -0
- package/README.md +463 -0
- package/dist/cli.js +1064 -0
- package/package.json +49 -0
- package/platforms/claude/adapter.ts +115 -0
- package/platforms/junie/adapter.ts +254 -0
- package/platforms/junie/agents-template.md +113 -0
- package/platforms/junie/hook-translations.md +84 -0
- package/platforms/shared/detector.ts +27 -0
- package/platforms/shared/installer.ts +202 -0
- package/platforms/shared/types.ts +78 -0
- package/plugins/ima-claude/.claude-plugin/plugin.json +25 -0
- package/plugins/ima-claude/agents/explorer.md +30 -0
- package/plugins/ima-claude/agents/implementer.md +30 -0
- package/plugins/ima-claude/agents/memory.md +42 -0
- package/plugins/ima-claude/agents/reviewer.md +53 -0
- package/plugins/ima-claude/agents/tester.md +33 -0
- package/plugins/ima-claude/agents/wp-developer.md +46 -0
- package/plugins/ima-claude/hooks/README.md +145 -0
- package/plugins/ima-claude/hooks/atlassian_prereqs.py +112 -0
- package/plugins/ima-claude/hooks/block_sed_edits.py +59 -0
- package/plugins/ima-claude/hooks/bootstrap.sh +90 -0
- package/plugins/ima-claude/hooks/bootstrap_utility_check.py +94 -0
- package/plugins/ima-claude/hooks/composer_autoload_check.py +70 -0
- package/plugins/ima-claude/hooks/docs_organization.py +104 -0
- package/plugins/ima-claude/hooks/enforce_rg_over_grep.py +56 -0
- package/plugins/ima-claude/hooks/fp_utility_check.py +90 -0
- package/plugins/ima-claude/hooks/hook_logger.py +69 -0
- package/plugins/ima-claude/hooks/hooks.json +239 -0
- package/plugins/ima-claude/hooks/jira_issue_fetch.py +79 -0
- package/plugins/ima-claude/hooks/jquery_in_wordpress.py +92 -0
- package/plugins/ima-claude/hooks/memory_bootstrap.py +79 -0
- package/plugins/ima-claude/hooks/memory_store_reminder.py +75 -0
- package/plugins/ima-claude/hooks/prompt_coach.py +125 -0
- package/plugins/ima-claude/hooks/prompt_coach_digest.md +48 -0
- package/plugins/ima-claude/hooks/prompt_coach_system.md +30 -0
- package/plugins/ima-claude/hooks/sequential_thinking_check.py +81 -0
- package/plugins/ima-claude/hooks/serena_over_grep.py +96 -0
- package/plugins/ima-claude/hooks/serena_over_read.py +66 -0
- package/plugins/ima-claude/hooks/serena_project_check.py +133 -0
- package/plugins/ima-claude/hooks/sql_injection_check.py +73 -0
- package/plugins/ima-claude/hooks/task_master_after_plan.py +31 -0
- package/plugins/ima-claude/hooks/task_master_before_impl.py +93 -0
- package/plugins/ima-claude/hooks/tavily_extract_advanced.py +48 -0
- package/plugins/ima-claude/hooks/vestige_before_external.py +86 -0
- package/plugins/ima-claude/hooks/webfetch_to_tavily.py +42 -0
- package/plugins/ima-claude/hooks/websearch_to_tavily.py +41 -0
- package/plugins/ima-claude/hooks/wp_security_check.py +150 -0
- package/plugins/ima-claude/personalities/README.md +45 -0
- package/plugins/ima-claude/personalities/enable-40k.md +69 -0
- package/plugins/ima-claude/personalities/enable-templars.md +69 -0
- package/plugins/ima-claude/skills/.research-summary.md +340 -0
- package/plugins/ima-claude/skills/architect/SKILL.md +304 -0
- package/plugins/ima-claude/skills/compound-bridge/SKILL.md +200 -0
- package/plugins/ima-claude/skills/discourse/SKILL.md +440 -0
- package/plugins/ima-claude/skills/discourse-admin/SKILL.md +192 -0
- package/plugins/ima-claude/skills/discourse-admin/references/api-endpoints.md +441 -0
- package/plugins/ima-claude/skills/discourse-admin/references/gotchas.md +107 -0
- package/plugins/ima-claude/skills/discourse-admin/references/staging-defaults.md +98 -0
- package/plugins/ima-claude/skills/discourse-admin/scripts/discourse-admin.py +319 -0
- package/plugins/ima-claude/skills/docs-organize/SKILL.md +254 -0
- package/plugins/ima-claude/skills/docs-organize/templates/active-README.md +50 -0
- package/plugins/ima-claude/skills/docs-organize/templates/archive-README.md +57 -0
- package/plugins/ima-claude/skills/docs-organize/templates/docs-README.md +43 -0
- package/plugins/ima-claude/skills/docs-organize/templates/phase-archive-README.md +83 -0
- package/plugins/ima-claude/skills/docs-organize/templates/section-README.md +48 -0
- package/plugins/ima-claude/skills/docs-organize/templates/transient-README.md +79 -0
- package/plugins/ima-claude/skills/docs-organize/templates/transient-gitignore +9 -0
- package/plugins/ima-claude/skills/ember-discourse/SKILL.md +496 -0
- package/plugins/ima-claude/skills/functional-programmer/SKILL.md +258 -0
- package/plugins/ima-claude/skills/ima-bootstrap/SKILL.md +278 -0
- package/plugins/ima-claude/skills/ima-bootstrap/references/bootstrap-patterns.md +356 -0
- package/plugins/ima-claude/skills/ima-bootstrap/references/ima-brand.md +273 -0
- package/plugins/ima-claude/skills/ima-bootstrap/references/theme-integration.md +212 -0
- package/plugins/ima-claude/skills/ima-brand/SKILL.md +108 -0
- package/plugins/ima-claude/skills/ima-brand/references/brand-identity.md +140 -0
- package/plugins/ima-claude/skills/ima-brand/references/digital-standards.md +180 -0
- package/plugins/ima-claude/skills/ima-brand/references/visual-system.md +173 -0
- package/plugins/ima-claude/skills/ima-forms-expert/SKILL.md +175 -0
- package/plugins/ima-claude/skills/ima-forms-expert/references/container-components.md +154 -0
- package/plugins/ima-claude/skills/ima-forms-expert/references/examples.md +328 -0
- package/plugins/ima-claude/skills/ima-forms-expert/references/field-components.md +298 -0
- package/plugins/ima-claude/skills/ima-forms-expert/references/form-factory.md +193 -0
- package/plugins/ima-claude/skills/ima-forms-expert/references/quick-reference.md +153 -0
- package/plugins/ima-claude/skills/ima-forms-expert/references/validation-engine.md +336 -0
- package/plugins/ima-claude/skills/jira-checkpoint/SKILL.md +178 -0
- package/plugins/ima-claude/skills/jquery/SKILL.md +413 -0
- package/plugins/ima-claude/skills/js-fp/SKILL.md +463 -0
- package/plugins/ima-claude/skills/js-fp/core-principles.md +487 -0
- package/plugins/ima-claude/skills/js-fp/examples/pure-functions.js +260 -0
- package/plugins/ima-claude/skills/js-fp/examples/tests/pure-functions.test.js +262 -0
- package/plugins/ima-claude/skills/js-fp/references/anti-patterns.md +120 -0
- package/plugins/ima-claude/skills/js-fp/references/performance-patterns.md +116 -0
- package/plugins/ima-claude/skills/js-fp/references/testing-patterns.md +134 -0
- package/plugins/ima-claude/skills/js-fp-api/SKILL.md +280 -0
- package/plugins/ima-claude/skills/js-fp-api/examples/crud-endpoint.js +258 -0
- package/plugins/ima-claude/skills/js-fp-api/references/middleware-patterns.md +134 -0
- package/plugins/ima-claude/skills/js-fp-api/references/security-sql.md +110 -0
- package/plugins/ima-claude/skills/js-fp-api/references/validation-patterns.md +165 -0
- package/plugins/ima-claude/skills/js-fp-react/SKILL.md +447 -0
- package/plugins/ima-claude/skills/js-fp-react/examples/ProductCard.tsx +65 -0
- package/plugins/ima-claude/skills/js-fp-react/references/hooks-advanced.md +136 -0
- package/plugins/ima-claude/skills/js-fp-react/references/performance-patterns.md +175 -0
- package/plugins/ima-claude/skills/js-fp-vue/SKILL.md +322 -0
- package/plugins/ima-claude/skills/js-fp-vue/references/complete-examples.md +397 -0
- package/plugins/ima-claude/skills/js-fp-vue/references/composables-advanced.md +282 -0
- package/plugins/ima-claude/skills/js-fp-vue/references/reactivity-patterns.md +348 -0
- package/plugins/ima-claude/skills/js-fp-vue/references/testing.md +314 -0
- package/plugins/ima-claude/skills/js-fp-wordpress/SKILL.md +301 -0
- package/plugins/ima-claude/skills/js-fp-wordpress/references/ajax-patterns.md +192 -0
- package/plugins/ima-claude/skills/js-fp-wordpress/references/event-patterns.md +136 -0
- package/plugins/ima-claude/skills/js-fp-wordpress/references/wp-integration.md +248 -0
- package/plugins/ima-claude/skills/livecanvas/SKILL.md +209 -0
- package/plugins/ima-claude/skills/livecanvas/references/livecanvas-features.md +311 -0
- package/plugins/ima-claude/skills/livecanvas/references/loops-and-logic.md +730 -0
- package/plugins/ima-claude/skills/livecanvas/references/picostrap.md +227 -0
- package/plugins/ima-claude/skills/mcp-atlassian/SKILL.md +339 -0
- package/plugins/ima-claude/skills/mcp-context7/SKILL.md +109 -0
- package/plugins/ima-claude/skills/mcp-memory/SKILL.md +182 -0
- package/plugins/ima-claude/skills/mcp-qdrant/SKILL.md +233 -0
- package/plugins/ima-claude/skills/mcp-sequential/SKILL.md +149 -0
- package/plugins/ima-claude/skills/mcp-serena/SKILL.md +174 -0
- package/plugins/ima-claude/skills/mcp-tavily/SKILL.md +118 -0
- package/plugins/ima-claude/skills/mcp-vestige/SKILL.md +259 -0
- package/plugins/ima-claude/skills/php-authnet/SKILL.md +275 -0
- package/plugins/ima-claude/skills/php-authnet/references/api-reference.md +624 -0
- package/plugins/ima-claude/skills/php-authnet/references/sandbox-testing.md +424 -0
- package/plugins/ima-claude/skills/php-fp/SKILL.md +333 -0
- package/plugins/ima-claude/skills/php-fp/examples/pure-functions.php +403 -0
- package/plugins/ima-claude/skills/php-fp/examples/tests/PureFunctionsTest.php +515 -0
- package/plugins/ima-claude/skills/php-fp/references/core-principles.md +277 -0
- package/plugins/ima-claude/skills/php-fp/references/testing-patterns.md +374 -0
- package/plugins/ima-claude/skills/php-fp-wordpress/SKILL.md +216 -0
- package/plugins/ima-claude/skills/php-fp-wordpress/references/fp-patterns.md +275 -0
- package/plugins/ima-claude/skills/php-fp-wordpress/references/plugin-architecture.md +295 -0
- package/plugins/ima-claude/skills/php-fp-wordpress/references/security-examples.md +203 -0
- package/plugins/ima-claude/skills/php-fp-wordpress/references/testing-strategy.md +259 -0
- package/plugins/ima-claude/skills/phpunit-wp/SKILL.md +716 -0
- package/plugins/ima-claude/skills/playwright/SKILL.md +434 -0
- package/plugins/ima-claude/skills/playwright/references/accessibility-testing.md +153 -0
- package/plugins/ima-claude/skills/playwright/references/ci-cd.md +268 -0
- package/plugins/ima-claude/skills/playwright/references/network-mocking.md +270 -0
- package/plugins/ima-claude/skills/playwright/references/visual-regression.md +215 -0
- package/plugins/ima-claude/skills/py-fp/SKILL.md +663 -0
- package/plugins/ima-claude/skills/py-fp/examples/pure-functions.py +185 -0
- package/plugins/ima-claude/skills/py-fp/examples/tests/test_pure_functions.py +244 -0
- package/plugins/ima-claude/skills/py-fp/references/core-principles.md +381 -0
- package/plugins/ima-claude/skills/py-fp/references/testing-patterns.md +283 -0
- package/plugins/ima-claude/skills/quasar-fp/SKILL.md +327 -0
- package/plugins/ima-claude/skills/quasar-fp/metadata.json +85 -0
- package/plugins/ima-claude/skills/quasar-fp/references/component-patterns.md +257 -0
- package/plugins/ima-claude/skills/quasar-fp/references/theme-integration.md +233 -0
- package/plugins/ima-claude/skills/quasar-fp/references/utility-classes.md +237 -0
- package/plugins/ima-claude/skills/quickstart/SKILL.md +129 -0
- package/plugins/ima-claude/skills/rails/SKILL.md +359 -0
- package/plugins/ima-claude/skills/resume-session/SKILL.md +68 -0
- package/plugins/ima-claude/skills/rg/SKILL.md +205 -0
- package/plugins/ima-claude/skills/ruby-fp/SKILL.md +336 -0
- package/plugins/ima-claude/skills/save-session/SKILL.md +81 -0
- package/plugins/ima-claude/skills/scorecard/SKILL.md +96 -0
- package/plugins/ima-claude/skills/skill-analyzer/SKILL.md +127 -0
- package/plugins/ima-claude/skills/skill-analyzer/references/advanced-checklist.md +44 -0
- package/plugins/ima-claude/skills/skill-analyzer/references/core-checklist.md +60 -0
- package/plugins/ima-claude/skills/skill-analyzer/scripts/analyze_skill.py +418 -0
- package/plugins/ima-claude/skills/skill-creator/LICENSE.txt +202 -0
- package/plugins/ima-claude/skills/skill-creator/SKILL.md +343 -0
- package/plugins/ima-claude/skills/skill-creator/references/output-patterns.md +82 -0
- package/plugins/ima-claude/skills/skill-creator/references/workflows.md +28 -0
- package/plugins/ima-claude/skills/skill-creator/scripts/init_skill.py +303 -0
- package/plugins/ima-claude/skills/skill-creator/scripts/package_skill.py +110 -0
- package/plugins/ima-claude/skills/skill-creator/scripts/quick_validate.py +103 -0
- package/plugins/ima-claude/skills/task-master/SKILL.md +51 -0
- package/plugins/ima-claude/skills/task-planner/SKILL.md +228 -0
- package/plugins/ima-claude/skills/task-runner/SKILL.md +192 -0
- package/plugins/ima-claude/skills/unit-testing/SKILL.md +198 -0
- package/plugins/ima-claude/skills/unit-testing/references/mock-patterns.md +181 -0
- package/plugins/ima-claude/skills/unit-testing/references/tdd-workflow.md +177 -0
- package/plugins/ima-claude/skills/unit-testing/references/test-strategy.md +126 -0
- package/plugins/ima-claude/skills/wp-local/SKILL.md +246 -0
- package/plugins/ima-claude/skills/wp-local/references/configuration.md +198 -0
- package/plugins/ima-claude/skills/wp-local/references/wp-cli-reference.md +406 -0
- package/plugins/ima-claude/skills/wp-local/scripts/wp-local.sh +61 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "php-fp-wordpress"
|
|
3
|
+
description: "Security-first WordPress development with PHP FP principles - pure business logic + WordPress integration"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# PHP FP - WordPress
|
|
7
|
+
|
|
8
|
+
Security-first WordPress development combining PHP functional programming principles with mandatory WordPress security practices.
|
|
9
|
+
|
|
10
|
+
## When to Use This Skill
|
|
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.
|
|
26
|
+
|
|
27
|
+
## The 5 Non-Negotiable Security Practices
|
|
28
|
+
|
|
29
|
+
**Evidence**: Analysis of 7,966 vulnerabilities (2024) shows these prevent 95%+ of WordPress plugin vulnerabilities.
|
|
30
|
+
|
|
31
|
+
| Practice | Prevents | Rule |
|
|
32
|
+
|----------|----------|------|
|
|
33
|
+
| **Capability Checks** | 53% of XSS | `current_user_can()` before ANY privileged operation |
|
|
34
|
+
| **Nonce Verification** | 15-17% CSRF | `wp_verify_nonce()` on ALL form/AJAX submissions |
|
|
35
|
+
| **Input Sanitization** | Injection | Sanitize ALL user input by type |
|
|
36
|
+
| **Output Escaping** | XSS | Escape ALL output by context |
|
|
37
|
+
| **Prepared Statements** | SQL injection | `$wpdb->prepare()` for ALL queries |
|
|
38
|
+
|
|
39
|
+
### Quick Reference: Security Functions
|
|
40
|
+
|
|
41
|
+
**Sanitization (Input)**:
|
|
42
|
+
| Function | Use For |
|
|
43
|
+
|----------|---------|
|
|
44
|
+
| `sanitize_text_field()` | Plain text |
|
|
45
|
+
| `sanitize_email()` | Email addresses |
|
|
46
|
+
| `absint()` | Positive integers |
|
|
47
|
+
| `wp_kses_post()` | HTML content |
|
|
48
|
+
| `esc_url_raw()` | URLs (for storage) |
|
|
49
|
+
|
|
50
|
+
**Escaping (Output)**:
|
|
51
|
+
| Function | Context |
|
|
52
|
+
|----------|---------|
|
|
53
|
+
| `esc_html()` | HTML body |
|
|
54
|
+
| `esc_attr()` | HTML attributes |
|
|
55
|
+
| `esc_url()` | URLs in href/src |
|
|
56
|
+
| `wp_json_encode()` | JavaScript data |
|
|
57
|
+
|
|
58
|
+
### Minimal Security Pattern
|
|
59
|
+
|
|
60
|
+
```php
|
|
61
|
+
<?php
|
|
62
|
+
add_action('wp_ajax_my_action', function() {
|
|
63
|
+
// 1. Capability check
|
|
64
|
+
if (!current_user_can('edit_posts')) {
|
|
65
|
+
wp_send_json_error('Unauthorized', 403);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// 2. Nonce verification
|
|
69
|
+
check_ajax_referer('my_action_nonce', 'nonce');
|
|
70
|
+
|
|
71
|
+
// 3. Sanitize input
|
|
72
|
+
$id = absint($_POST['id']);
|
|
73
|
+
$name = sanitize_text_field($_POST['name']);
|
|
74
|
+
|
|
75
|
+
// 4. Use prepared statement
|
|
76
|
+
global $wpdb;
|
|
77
|
+
$result = $wpdb->get_row($wpdb->prepare(
|
|
78
|
+
"SELECT * FROM {$wpdb->prefix}my_table WHERE id = %d",
|
|
79
|
+
$id
|
|
80
|
+
));
|
|
81
|
+
|
|
82
|
+
// 5. Escape output
|
|
83
|
+
wp_send_json_success(['name' => esc_html($result->name)]);
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Pure Logic + WordPress Wrapper Pattern
|
|
88
|
+
|
|
89
|
+
**Separate testable business logic from WordPress integration.**
|
|
90
|
+
|
|
91
|
+
```php
|
|
92
|
+
<?php
|
|
93
|
+
// PURE: Zero WordPress dependencies, fully testable
|
|
94
|
+
namespace MyPlugin\Pure;
|
|
95
|
+
|
|
96
|
+
function calculate_discount(float $price, string $tier): float {
|
|
97
|
+
$rates = ['bronze' => 0.05, 'silver' => 0.10, 'gold' => 0.15];
|
|
98
|
+
return round($price * (1 - ($rates[$tier] ?? 0)), 2);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// WRAPPER: WordPress integration with security
|
|
102
|
+
namespace MyPlugin;
|
|
103
|
+
|
|
104
|
+
add_filter('product_price', function($price) {
|
|
105
|
+
if (!is_user_logged_in()) return $price;
|
|
106
|
+
|
|
107
|
+
$tier = get_user_meta(get_current_user_id(), 'tier', true);
|
|
108
|
+
return Pure\calculate_discount($price, $tier);
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Inter-Plugin Communication: Hooks Only
|
|
113
|
+
|
|
114
|
+
**Rule**: ALL cross-plugin calls use WordPress hooks. NEVER `function_exists()`.
|
|
115
|
+
|
|
116
|
+
Hooks are safe no-ops — if nobody listens, nothing happens. `function_exists()` is tight coupling disguised as loose coupling.
|
|
117
|
+
|
|
118
|
+
```php
|
|
119
|
+
<?php
|
|
120
|
+
// BAD — tight coupling, breaks silently on rename
|
|
121
|
+
if (function_exists('ima_discourse_refresh_user_meta')) {
|
|
122
|
+
ima_discourse_refresh_user_meta($user_id);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// GOOD — fire-and-forget side effect
|
|
126
|
+
do_action('ima_discourse_refresh_user_meta', $user_id);
|
|
127
|
+
|
|
128
|
+
// GOOD — transform with safe default (function composition via WP)
|
|
129
|
+
$result = apply_filters('ima_membership_cancel_subscription', ['success' => true], $user_id, $sub_id);
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Actions** (`do_action`): Side effects — "something happened, react if you care."
|
|
133
|
+
**Filters** (`apply_filters`): Data transformation — chained function composition with a default return.
|
|
134
|
+
|
|
135
|
+
The handler registers itself:
|
|
136
|
+
```php
|
|
137
|
+
<?php
|
|
138
|
+
// ima-discourse registers once — or doesn't. Either way, callers don't crash.
|
|
139
|
+
add_action('ima_discourse_refresh_user_meta', 'ima_discourse_refresh_user_meta', 10, 1);
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**When `function_exists()` is acceptable**: Internal guard clauses within a single plugin checking if its own functions are loaded, or checking PHP extensions (`function_exists('sodium_crypto_secretbox')`).
|
|
143
|
+
|
|
144
|
+
## Plugin Complexity Guide
|
|
145
|
+
|
|
146
|
+
| Size | Lines | Pattern |
|
|
147
|
+
|------|-------|---------|
|
|
148
|
+
| Simple | <500 | Namespaced functions |
|
|
149
|
+
| Medium | 500-2000 | Classes + pure functions |
|
|
150
|
+
| Complex | 2000+ | DI Container + Services |
|
|
151
|
+
|
|
152
|
+
**Rule**: Start simple, add complexity only when needed.
|
|
153
|
+
|
|
154
|
+
## File Organization
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
my-plugin/
|
|
158
|
+
├── my-plugin.php # Bootstrap
|
|
159
|
+
├── includes/
|
|
160
|
+
│ └── functions.php # Pure business logic
|
|
161
|
+
├── admin/
|
|
162
|
+
│ └── ajax-handlers.php # WordPress integration
|
|
163
|
+
└── tests/
|
|
164
|
+
├── unit/ # Pure function tests (fast)
|
|
165
|
+
└── integration/ # WordPress tests
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Security Checklist
|
|
169
|
+
|
|
170
|
+
- [ ] Capability checks on all privileged operations
|
|
171
|
+
- [ ] Nonces on all form/AJAX submissions
|
|
172
|
+
- [ ] Input sanitized by type
|
|
173
|
+
- [ ] Output escaped by context
|
|
174
|
+
- [ ] SQL uses `$wpdb->prepare()`
|
|
175
|
+
- [ ] File uploads use `wp_handle_upload()`
|
|
176
|
+
- [ ] No hardcoded credentials
|
|
177
|
+
- [ ] Cross-plugin calls use hooks, not `function_exists()`
|
|
178
|
+
|
|
179
|
+
## Quality Gates
|
|
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
|
|
209
|
+
|
|
210
|
+
- **Security**: Zero vulnerabilities from missing practices
|
|
211
|
+
- **Testability**: 95%+ coverage for pure functions
|
|
212
|
+
- **Maintainability**: Clear separation of concerns
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
**Evidence Base**: Analysis of 7,966 WordPress vulnerabilities (2024), WordPress Core Team standards, Wordfence/Patchstack research.
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# Pure Logic + WordPress Wrapper Patterns
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
1. [Core Pattern](#core-pattern)
|
|
5
|
+
2. [Complete Example: Membership System](#complete-example-membership-system)
|
|
6
|
+
3. [Function Factory Pattern](#function-factory-pattern)
|
|
7
|
+
4. [Production Example: Email Validator](#production-example-email-validator)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Core Pattern
|
|
12
|
+
|
|
13
|
+
**Separate pure business logic from WordPress integration.**
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
┌─────────────────────────────────────────┐
|
|
17
|
+
│ PURE BUSINESS LOGIC │
|
|
18
|
+
│ • Zero WordPress dependencies │
|
|
19
|
+
│ • Deterministic, testable │
|
|
20
|
+
│ • Returns data, no side effects │
|
|
21
|
+
└─────────────────────────────────────────┘
|
|
22
|
+
│
|
|
23
|
+
▼
|
|
24
|
+
┌─────────────────────────────────────────┐
|
|
25
|
+
│ WORDPRESS WRAPPER │
|
|
26
|
+
│ • Security checks (capability, nonce) │
|
|
27
|
+
│ • Input sanitization │
|
|
28
|
+
│ • Database operations │
|
|
29
|
+
│ • Output escaping │
|
|
30
|
+
└─────────────────────────────────────────┘
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Complete Example: Membership System
|
|
36
|
+
|
|
37
|
+
### Pure Business Logic (Zero WordPress Dependencies)
|
|
38
|
+
|
|
39
|
+
```php
|
|
40
|
+
<?php
|
|
41
|
+
namespace MyPlugin\BusinessLogic;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Calculate membership discount
|
|
45
|
+
* @pure - No side effects, deterministic, easily testable
|
|
46
|
+
*/
|
|
47
|
+
function calculate_discount(float $price, string $tier, int $days_member): float {
|
|
48
|
+
$tier_multipliers = [
|
|
49
|
+
'bronze' => 0.05,
|
|
50
|
+
'silver' => 0.10,
|
|
51
|
+
'gold' => 0.15,
|
|
52
|
+
'platinum' => 0.20
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
$base_discount = $tier_multipliers[$tier] ?? 0;
|
|
56
|
+
$loyalty_bonus = min($days_member / 365 * 0.02, 0.05); // Max 5% loyalty
|
|
57
|
+
$total_discount = min($base_discount + $loyalty_bonus, 0.25); // Max 25%
|
|
58
|
+
|
|
59
|
+
return round($price * (1 - $total_discount), 2);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Validate membership tier upgrade
|
|
64
|
+
* @pure - Returns validation result, no database queries
|
|
65
|
+
*/
|
|
66
|
+
function validate_tier_upgrade(string $current_tier, string $new_tier): array {
|
|
67
|
+
$tier_hierarchy = ['bronze', 'silver', 'gold', 'platinum'];
|
|
68
|
+
$current_level = array_search($current_tier, $tier_hierarchy);
|
|
69
|
+
$new_level = array_search($new_tier, $tier_hierarchy);
|
|
70
|
+
|
|
71
|
+
if ($current_level === false || $new_level === false) {
|
|
72
|
+
return ['valid' => false, 'error' => 'Invalid tier specified'];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if ($new_level <= $current_level) {
|
|
76
|
+
return ['valid' => false, 'error' => 'Cannot downgrade or stay at same tier'];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return ['valid' => true, 'upgrade_levels' => $new_level - $current_level];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Format membership data for display
|
|
84
|
+
* @pure - Data transformation only
|
|
85
|
+
*/
|
|
86
|
+
function format_membership_display(array $membership): array {
|
|
87
|
+
return [
|
|
88
|
+
'tier' => ucfirst($membership['tier']),
|
|
89
|
+
'expires' => date('F j, Y', strtotime($membership['expires'])),
|
|
90
|
+
'days_remaining' => max(0, (strtotime($membership['expires']) - time()) / DAY_IN_SECONDS),
|
|
91
|
+
'status' => strtotime($membership['expires']) > time() ? 'Active' : 'Expired'
|
|
92
|
+
];
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### WordPress Integration Layer
|
|
97
|
+
|
|
98
|
+
```php
|
|
99
|
+
<?php
|
|
100
|
+
namespace MyPlugin;
|
|
101
|
+
|
|
102
|
+
use MyPlugin\BusinessLogic;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* WordPress filter hook - uses pure function
|
|
106
|
+
*/
|
|
107
|
+
add_filter('woocommerce_product_price', function($price, $product) {
|
|
108
|
+
if (!is_user_logged_in()) {
|
|
109
|
+
return $price;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
$user_id = get_current_user_id();
|
|
113
|
+
$tier = get_user_meta($user_id, 'membership_tier', true);
|
|
114
|
+
$member_since = get_user_meta($user_id, 'member_since', true);
|
|
115
|
+
$days_member = (time() - strtotime($member_since)) / DAY_IN_SECONDS;
|
|
116
|
+
|
|
117
|
+
// Pure function call - easily testable
|
|
118
|
+
return BusinessLogic\calculate_discount($price, $tier, $days_member);
|
|
119
|
+
}, 10, 2);
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* WordPress action hook - handles side effects with security
|
|
123
|
+
*/
|
|
124
|
+
add_action('wp_ajax_upgrade_membership', function() {
|
|
125
|
+
// 1. Security first - capability check
|
|
126
|
+
if (!current_user_can('edit_user')) {
|
|
127
|
+
wp_send_json_error('Insufficient permissions', 403);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// 2. Security first - nonce verification
|
|
131
|
+
check_ajax_referer('upgrade_membership', 'nonce');
|
|
132
|
+
|
|
133
|
+
// 3. Sanitize inputs
|
|
134
|
+
$user_id = absint($_POST['user_id']);
|
|
135
|
+
$new_tier = sanitize_text_field($_POST['new_tier']);
|
|
136
|
+
|
|
137
|
+
// 4. Get current data (side effect)
|
|
138
|
+
$current_tier = get_user_meta($user_id, 'membership_tier', true);
|
|
139
|
+
|
|
140
|
+
// 5. Use pure function for validation
|
|
141
|
+
$validation = BusinessLogic\validate_tier_upgrade($current_tier, $new_tier);
|
|
142
|
+
|
|
143
|
+
if (!$validation['valid']) {
|
|
144
|
+
wp_send_json_error($validation['error']);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// 6. Update database (side effect)
|
|
148
|
+
update_user_meta($user_id, 'membership_tier', $new_tier);
|
|
149
|
+
update_user_meta($user_id, 'tier_upgraded_at', current_time('mysql'));
|
|
150
|
+
|
|
151
|
+
// 7. Send response (escaped)
|
|
152
|
+
wp_send_json_success([
|
|
153
|
+
'message' => 'Membership upgraded successfully',
|
|
154
|
+
'new_tier' => esc_html($new_tier)
|
|
155
|
+
]);
|
|
156
|
+
});
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Function Factory Pattern
|
|
162
|
+
|
|
163
|
+
**Pre-compile configuration for performance.**
|
|
164
|
+
|
|
165
|
+
```php
|
|
166
|
+
<?php
|
|
167
|
+
namespace MyPlugin\Pure;
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Function factory: Pre-compile email validator
|
|
171
|
+
* @pure - Configuration captured in closure
|
|
172
|
+
*/
|
|
173
|
+
function create_email_validator(
|
|
174
|
+
array $bad_domains,
|
|
175
|
+
array $typo_corrections
|
|
176
|
+
): callable {
|
|
177
|
+
// Configuration pre-compiled once during factory creation
|
|
178
|
+
return function (string $email) use ($bad_domains, $typo_corrections): array {
|
|
179
|
+
return validate_email_domain($email, $bad_domains, $typo_corrections);
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Validate email domain
|
|
185
|
+
* @pure - Deterministic, no WordPress dependencies
|
|
186
|
+
*/
|
|
187
|
+
function validate_email_domain(
|
|
188
|
+
string $email,
|
|
189
|
+
array $bad_domains,
|
|
190
|
+
array $typo_domains
|
|
191
|
+
): array {
|
|
192
|
+
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
|
193
|
+
return ['valid' => false, 'error' => 'Invalid email format'];
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
$domain = strtolower(substr($email, strpos($email, '@') + 1));
|
|
197
|
+
|
|
198
|
+
if (in_array($domain, $bad_domains, true)) {
|
|
199
|
+
return ['valid' => false, 'error' => 'Disposable email domain'];
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (isset($typo_domains[$domain])) {
|
|
203
|
+
return [
|
|
204
|
+
'valid' => false,
|
|
205
|
+
'error' => 'Possible typo',
|
|
206
|
+
'suggestion' => $typo_domains[$domain]
|
|
207
|
+
];
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return ['valid' => true];
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Production Example: Email Validator
|
|
217
|
+
|
|
218
|
+
**From ima-espo plugin - 130 tests, <30ms execution.**
|
|
219
|
+
|
|
220
|
+
```php
|
|
221
|
+
<?php
|
|
222
|
+
use function MyPlugin\Pure\create_email_validator;
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* WordPress wrapper class using function factory pattern
|
|
226
|
+
*/
|
|
227
|
+
class EmailValidatorCore {
|
|
228
|
+
protected $validator = null;
|
|
229
|
+
protected $bad_domains = [];
|
|
230
|
+
protected $typo_domains = [];
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Constructor: Pre-compile validator function once
|
|
234
|
+
*/
|
|
235
|
+
public function __construct(array $bad_domains = [], array $typo_domains = []) {
|
|
236
|
+
$this->bad_domains = $bad_domains;
|
|
237
|
+
$this->typo_domains = $typo_domains;
|
|
238
|
+
|
|
239
|
+
// Function factory: Configuration pre-compiled once
|
|
240
|
+
// Performance: 2-5x speedup when validating multiple emails
|
|
241
|
+
$this->validator = create_email_validator($bad_domains, $typo_domains);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* WordPress integration method
|
|
246
|
+
*/
|
|
247
|
+
public function validate_email_domain(string $email) {
|
|
248
|
+
// Security: Sanitize input
|
|
249
|
+
$email = sanitize_email($email);
|
|
250
|
+
|
|
251
|
+
// Use pre-compiled validator
|
|
252
|
+
$result = ($this->validator)($email);
|
|
253
|
+
|
|
254
|
+
// WordPress integration: Log failures in debug mode
|
|
255
|
+
if (!$result['valid'] && defined('WP_DEBUG') && WP_DEBUG) {
|
|
256
|
+
error_log(sprintf('[Plugin] Email validation failed: %s - %s', $email, $result['error']));
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return $result;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Allow customization via WordPress filters
|
|
264
|
+
*/
|
|
265
|
+
public function get_bad_domains(): array {
|
|
266
|
+
return apply_filters('my_plugin_bad_email_domains', $this->bad_domains);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Benefits
|
|
272
|
+
- **Performance**: 2-5x faster (configuration pre-compiled once)
|
|
273
|
+
- **Testability**: Pure functions fully testable without WordPress
|
|
274
|
+
- **Security**: Sanitization at wrapper boundary
|
|
275
|
+
- **Extensibility**: WordPress filters allow customization
|