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,295 @@
|
|
|
1
|
+
# Plugin Complexity Patterns
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
1. [Complexity Decision Guide](#complexity-decision-guide)
|
|
5
|
+
2. [Simple Plugin (<500 lines)](#simple-plugin-500-lines)
|
|
6
|
+
3. [Medium Plugin (500-2000 lines)](#medium-plugin-500-2000-lines)
|
|
7
|
+
4. [Complex Plugin (2000+ lines)](#complex-plugin-2000-lines)
|
|
8
|
+
5. [File Organization](#file-organization)
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Complexity Decision Guide
|
|
13
|
+
|
|
14
|
+
| Plugin Size | Lines | Pattern | When to Use |
|
|
15
|
+
|-------------|-------|---------|-------------|
|
|
16
|
+
| Simple | <500 | Namespaced functions | Single feature, few hooks |
|
|
17
|
+
| Medium | 500-2000 | Classes + pure functions | Multiple features, some state |
|
|
18
|
+
| Complex | 2000+ | DI Container + Services | Many features, shared dependencies |
|
|
19
|
+
|
|
20
|
+
**Rule**: Start simple, add complexity only when needed.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Simple Plugin (<500 lines)
|
|
25
|
+
|
|
26
|
+
**Use**: Namespaced functions with direct hook usage.
|
|
27
|
+
|
|
28
|
+
```php
|
|
29
|
+
<?php
|
|
30
|
+
/**
|
|
31
|
+
* Plugin Name: Simple Analytics Tracker
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
namespace SimpleAnalytics;
|
|
35
|
+
|
|
36
|
+
// Pure function - testable without WordPress
|
|
37
|
+
function calculate_page_views(array $logs): int {
|
|
38
|
+
return count(array_filter($logs, fn($log) => $log['type'] === 'page_view'));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function format_analytics_data(array $logs): array {
|
|
42
|
+
return [
|
|
43
|
+
'page_views' => calculate_page_views($logs),
|
|
44
|
+
'unique_visitors' => count(array_unique(array_column($logs, 'visitor_id'))),
|
|
45
|
+
'last_updated' => date('Y-m-d H:i:s')
|
|
46
|
+
];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// WordPress integration with security
|
|
50
|
+
add_action('wp_footer', function() {
|
|
51
|
+
if (!current_user_can('edit_posts')) return;
|
|
52
|
+
|
|
53
|
+
$logs = get_option('analytics_logs', []);
|
|
54
|
+
$data = format_analytics_data($logs);
|
|
55
|
+
|
|
56
|
+
echo '<div id="analytics">' . esc_html($data['page_views']) . ' page views</div>';
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
add_action('wp_ajax_get_analytics', function() {
|
|
60
|
+
if (!current_user_can('manage_options')) {
|
|
61
|
+
wp_send_json_error('Unauthorized', 403);
|
|
62
|
+
}
|
|
63
|
+
check_ajax_referer('analytics_nonce');
|
|
64
|
+
|
|
65
|
+
$logs = get_option('analytics_logs', []);
|
|
66
|
+
wp_send_json_success(format_analytics_data($logs));
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Medium Plugin (500-2000 lines)
|
|
73
|
+
|
|
74
|
+
**Use**: Classes for features + pure functions for logic.
|
|
75
|
+
|
|
76
|
+
```php
|
|
77
|
+
<?php
|
|
78
|
+
namespace MediumPlugin;
|
|
79
|
+
|
|
80
|
+
// Pure functions in separate file (includes/functions.php)
|
|
81
|
+
function calculate_shipping_cost(float $weight, string $zone): float {
|
|
82
|
+
$rates = ['domestic' => 5.00, 'international' => 15.00];
|
|
83
|
+
$base = $rates[$zone] ?? 10.00;
|
|
84
|
+
return $base + ($weight * 0.50);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function validate_shipping_address(array $address): array {
|
|
88
|
+
$errors = [];
|
|
89
|
+
if (empty($address['street'])) $errors[] = 'Street required';
|
|
90
|
+
if (empty($address['city'])) $errors[] = 'City required';
|
|
91
|
+
if (empty($address['zip'])) $errors[] = 'ZIP required';
|
|
92
|
+
|
|
93
|
+
return empty($errors)
|
|
94
|
+
? ['valid' => true]
|
|
95
|
+
: ['valid' => false, 'errors' => $errors];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Class for WordPress integration
|
|
99
|
+
class ShippingCalculator {
|
|
100
|
+
public function __construct() {
|
|
101
|
+
add_filter('woocommerce_shipping_cost', [$this, 'apply_calculation'], 10, 2);
|
|
102
|
+
add_action('wp_ajax_validate_address', [$this, 'handle_validation']);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
public function apply_calculation($cost, $package) {
|
|
106
|
+
$weight = $package['contents_weight'];
|
|
107
|
+
$zone = $package['destination']['country'] === 'US' ? 'domestic' : 'international';
|
|
108
|
+
|
|
109
|
+
return calculate_shipping_cost($weight, $zone);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
public function handle_validation() {
|
|
113
|
+
if (!current_user_can('edit_shop_orders')) {
|
|
114
|
+
wp_send_json_error('Unauthorized', 403);
|
|
115
|
+
}
|
|
116
|
+
check_ajax_referer('shipping_nonce');
|
|
117
|
+
|
|
118
|
+
$address = array_map('sanitize_text_field', $_POST['address']);
|
|
119
|
+
$result = validate_shipping_address($address);
|
|
120
|
+
|
|
121
|
+
if ($result['valid']) {
|
|
122
|
+
wp_send_json_success('Address valid');
|
|
123
|
+
} else {
|
|
124
|
+
wp_send_json_error($result['errors']);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Initialize
|
|
130
|
+
new ShippingCalculator();
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Complex Plugin (2000+ lines)
|
|
136
|
+
|
|
137
|
+
**Use**: Dependency Injection Container + Service Architecture.
|
|
138
|
+
|
|
139
|
+
### DI Container
|
|
140
|
+
|
|
141
|
+
```php
|
|
142
|
+
<?php
|
|
143
|
+
namespace ComplexPlugin;
|
|
144
|
+
|
|
145
|
+
class Container {
|
|
146
|
+
private array $services = [];
|
|
147
|
+
private array $resolved = [];
|
|
148
|
+
|
|
149
|
+
public function register(string $name, callable $resolver): void {
|
|
150
|
+
$this->services[$name] = $resolver;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
public function get(string $name) {
|
|
154
|
+
if (!isset($this->resolved[$name])) {
|
|
155
|
+
$this->resolved[$name] = ($this->services[$name])($this);
|
|
156
|
+
}
|
|
157
|
+
return $this->resolved[$name];
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Service Classes
|
|
163
|
+
|
|
164
|
+
```php
|
|
165
|
+
<?php
|
|
166
|
+
namespace ComplexPlugin\Services;
|
|
167
|
+
|
|
168
|
+
class OrderService {
|
|
169
|
+
private PaymentService $payment;
|
|
170
|
+
|
|
171
|
+
public function __construct(PaymentService $payment) {
|
|
172
|
+
$this->payment = $payment;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
public function process(array $order_data): array {
|
|
176
|
+
// Use pure function for calculation
|
|
177
|
+
$total = \ComplexPlugin\BusinessLogic\calculate_order_total(
|
|
178
|
+
$order_data['items'],
|
|
179
|
+
$order_data['discounts']
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
// Side effect: process payment
|
|
183
|
+
$payment_result = $this->payment->charge($total);
|
|
184
|
+
|
|
185
|
+
return [
|
|
186
|
+
'order_id' => uniqid('order_'),
|
|
187
|
+
'total' => $total,
|
|
188
|
+
'payment' => $payment_result
|
|
189
|
+
];
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
class PaymentService {
|
|
194
|
+
public function charge(float $amount): array {
|
|
195
|
+
// Payment gateway integration
|
|
196
|
+
return ['success' => true, 'transaction_id' => uniqid('txn_')];
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Main Plugin Class
|
|
202
|
+
|
|
203
|
+
```php
|
|
204
|
+
<?php
|
|
205
|
+
namespace ComplexPlugin;
|
|
206
|
+
|
|
207
|
+
class Plugin {
|
|
208
|
+
private Container $container;
|
|
209
|
+
|
|
210
|
+
public function __construct() {
|
|
211
|
+
$this->container = new Container();
|
|
212
|
+
$this->setup_services();
|
|
213
|
+
$this->init_hooks();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
private function setup_services(): void {
|
|
217
|
+
$this->container->register('payment', fn($c) =>
|
|
218
|
+
new Services\PaymentService()
|
|
219
|
+
);
|
|
220
|
+
$this->container->register('order', fn($c) =>
|
|
221
|
+
new Services\OrderService($c->get('payment'))
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
private function init_hooks(): void {
|
|
226
|
+
add_action('wp_ajax_process_order', [$this, 'handle_order']);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
public function handle_order(): void {
|
|
230
|
+
// Security
|
|
231
|
+
if (!current_user_can('edit_shop_orders')) {
|
|
232
|
+
wp_send_json_error('Unauthorized', 403);
|
|
233
|
+
}
|
|
234
|
+
check_ajax_referer('process_order');
|
|
235
|
+
|
|
236
|
+
// Sanitize
|
|
237
|
+
$order_data = [
|
|
238
|
+
'items' => array_map(function($item) {
|
|
239
|
+
return [
|
|
240
|
+
'id' => absint($item['id']),
|
|
241
|
+
'price' => floatval($item['price']),
|
|
242
|
+
'quantity' => absint($item['quantity'])
|
|
243
|
+
];
|
|
244
|
+
}, $_POST['items']),
|
|
245
|
+
'discounts' => []
|
|
246
|
+
];
|
|
247
|
+
|
|
248
|
+
// Process via service
|
|
249
|
+
$result = $this->container->get('order')->process($order_data);
|
|
250
|
+
|
|
251
|
+
wp_send_json_success($result);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## File Organization
|
|
259
|
+
|
|
260
|
+
### Simple Plugin
|
|
261
|
+
```
|
|
262
|
+
simple-plugin/
|
|
263
|
+
├── simple-plugin.php # Everything in one file
|
|
264
|
+
└── readme.txt
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### Medium Plugin
|
|
268
|
+
```
|
|
269
|
+
medium-plugin/
|
|
270
|
+
├── medium-plugin.php # Bootstrap, hooks
|
|
271
|
+
├── includes/
|
|
272
|
+
│ ├── functions.php # Pure business logic
|
|
273
|
+
│ └── class-feature.php # WordPress integration
|
|
274
|
+
└── tests/
|
|
275
|
+
└── test-functions.php
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Complex Plugin
|
|
279
|
+
```
|
|
280
|
+
complex-plugin/
|
|
281
|
+
├── complex-plugin.php
|
|
282
|
+
├── includes/
|
|
283
|
+
│ ├── class-container.php
|
|
284
|
+
│ ├── class-plugin.php
|
|
285
|
+
│ └── functions.php # Pure business logic
|
|
286
|
+
├── services/
|
|
287
|
+
│ ├── class-order-service.php
|
|
288
|
+
│ └── class-payment-service.php
|
|
289
|
+
├── admin/
|
|
290
|
+
│ ├── settings.php
|
|
291
|
+
│ └── ajax-handlers.php
|
|
292
|
+
└── tests/
|
|
293
|
+
├── unit/ # Pure function tests
|
|
294
|
+
└── integration/ # WordPress tests
|
|
295
|
+
```
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# WordPress Security Patterns - Detailed Examples
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
1. [Capability Checks](#1-capability-checks)
|
|
5
|
+
2. [Nonce Verification](#2-nonce-verification)
|
|
6
|
+
3. [Input Sanitization](#3-input-sanitization)
|
|
7
|
+
4. [Output Escaping](#4-output-escaping)
|
|
8
|
+
5. [Prepared SQL Statements](#5-prepared-sql-statements)
|
|
9
|
+
6. [Security Function Reference](#security-function-reference)
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 1. Capability Checks
|
|
14
|
+
|
|
15
|
+
**Prevents 53% of XSS vulnerabilities.**
|
|
16
|
+
|
|
17
|
+
```php
|
|
18
|
+
<?php
|
|
19
|
+
// ALWAYS check permissions FIRST
|
|
20
|
+
add_action('wp_ajax_delete_user_data', 'handle_delete_user_data');
|
|
21
|
+
function handle_delete_user_data() {
|
|
22
|
+
// Check capability before ANY operation
|
|
23
|
+
if (!current_user_can('delete_users')) {
|
|
24
|
+
wp_send_json_error('Insufficient permissions', 403);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Then proceed
|
|
29
|
+
delete_user_data($_POST['user_id']);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// NEVER allow operations without capability check
|
|
33
|
+
function delete_user_data_UNSAFE() {
|
|
34
|
+
wp_delete_user($_POST['user_id']); // Any authenticated user can delete!
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Common Capabilities
|
|
39
|
+
| Capability | Use For |
|
|
40
|
+
|------------|---------|
|
|
41
|
+
| `manage_options` | Plugin settings, admin-only operations |
|
|
42
|
+
| `edit_posts` | Content creation |
|
|
43
|
+
| `delete_users` | User management |
|
|
44
|
+
| `upload_files` | File uploads |
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 2. Nonce Verification
|
|
49
|
+
|
|
50
|
+
**Prevents 15-17% CSRF attacks.**
|
|
51
|
+
|
|
52
|
+
```php
|
|
53
|
+
<?php
|
|
54
|
+
// ALWAYS verify nonces
|
|
55
|
+
add_action('admin_post_save_settings', 'save_plugin_settings');
|
|
56
|
+
function save_plugin_settings() {
|
|
57
|
+
// Verify nonce before processing
|
|
58
|
+
if (!isset($_POST['settings_nonce']) ||
|
|
59
|
+
!wp_verify_nonce($_POST['settings_nonce'], 'save_settings_action')) {
|
|
60
|
+
wp_die('Security check failed');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Then save
|
|
64
|
+
update_option('plugin_settings', $_POST['settings']);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// NEVER process forms without nonce
|
|
68
|
+
function save_settings_UNSAFE() {
|
|
69
|
+
update_option('plugin_settings', $_POST['settings']); // CSRF vulnerable!
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Nonce Functions
|
|
74
|
+
| Function | Purpose |
|
|
75
|
+
|----------|---------|
|
|
76
|
+
| `wp_nonce_field('action', 'field_name')` | Generate hidden form field |
|
|
77
|
+
| `wp_verify_nonce($_POST['field'], 'action')` | Verify form submission |
|
|
78
|
+
| `check_ajax_referer('action', 'nonce')` | Verify AJAX nonce |
|
|
79
|
+
| `wp_create_nonce('action')` | Create nonce for JS |
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## 3. Input Sanitization
|
|
84
|
+
|
|
85
|
+
**Required for ALL user input.**
|
|
86
|
+
|
|
87
|
+
```php
|
|
88
|
+
<?php
|
|
89
|
+
// ALWAYS sanitize based on data type
|
|
90
|
+
function process_form_submission() {
|
|
91
|
+
$name = sanitize_text_field($_POST['name']);
|
|
92
|
+
$email = sanitize_email($_POST['email']);
|
|
93
|
+
$content = wp_kses_post($_POST['content']); // Allow safe HTML
|
|
94
|
+
$url = esc_url_raw($_POST['website']);
|
|
95
|
+
$number = absint($_POST['count']);
|
|
96
|
+
|
|
97
|
+
save_to_database($name, $email, $content, $url, $number);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// NEVER use raw input
|
|
101
|
+
function process_form_UNSAFE() {
|
|
102
|
+
save_to_database($_POST['name'], $_POST['email']); // Injection risk!
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Sanitization Functions
|
|
107
|
+
| Function | Use For |
|
|
108
|
+
|----------|---------|
|
|
109
|
+
| `sanitize_text_field()` | Plain text, single line |
|
|
110
|
+
| `sanitize_textarea_field()` | Multi-line text |
|
|
111
|
+
| `sanitize_email()` | Email addresses |
|
|
112
|
+
| `absint()` | Positive integers |
|
|
113
|
+
| `wp_kses_post()` | HTML (post content allowed tags) |
|
|
114
|
+
| `esc_url_raw()` | URLs for database storage |
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## 4. Output Escaping
|
|
119
|
+
|
|
120
|
+
**Context-specific escaping required.**
|
|
121
|
+
|
|
122
|
+
```php
|
|
123
|
+
<?php
|
|
124
|
+
// ALWAYS escape based on context
|
|
125
|
+
function render_user_profile($user) {
|
|
126
|
+
// HTML context
|
|
127
|
+
echo '<h2>' . esc_html($user->name) . '</h2>';
|
|
128
|
+
|
|
129
|
+
// Attribute context
|
|
130
|
+
echo '<img src="' . esc_url($user->avatar) . '" alt="' . esc_attr($user->name) . '">';
|
|
131
|
+
|
|
132
|
+
// JavaScript context
|
|
133
|
+
echo '<script>var userName = ' . wp_json_encode($user->name) . ';</script>';
|
|
134
|
+
|
|
135
|
+
// URL context
|
|
136
|
+
echo '<a href="' . esc_url($user->website) . '">Website</a>';
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// NEVER output unescaped data
|
|
140
|
+
function render_profile_UNSAFE($user) {
|
|
141
|
+
echo '<h1>' . $user->name . '</h1>'; // XSS if name contains <script>
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Escaping Functions
|
|
146
|
+
| Function | Context |
|
|
147
|
+
|----------|---------|
|
|
148
|
+
| `esc_html()` | HTML body text |
|
|
149
|
+
| `esc_attr()` | HTML attributes |
|
|
150
|
+
| `esc_url()` | URLs in href/src |
|
|
151
|
+
| `esc_js()` | Inline JavaScript strings |
|
|
152
|
+
| `wp_json_encode()` | JSON data in scripts |
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## 5. Prepared SQL Statements
|
|
157
|
+
|
|
158
|
+
**Prevents SQL injection.**
|
|
159
|
+
|
|
160
|
+
```php
|
|
161
|
+
<?php
|
|
162
|
+
// ALWAYS use prepared statements
|
|
163
|
+
function get_user_posts($user_id) {
|
|
164
|
+
global $wpdb;
|
|
165
|
+
|
|
166
|
+
$posts = $wpdb->get_results($wpdb->prepare(
|
|
167
|
+
"SELECT * FROM {$wpdb->posts} WHERE post_author = %d AND post_status = %s",
|
|
168
|
+
$user_id,
|
|
169
|
+
'publish'
|
|
170
|
+
));
|
|
171
|
+
|
|
172
|
+
return $posts;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// NEVER use string concatenation
|
|
176
|
+
function get_posts_UNSAFE($user_id) {
|
|
177
|
+
global $wpdb;
|
|
178
|
+
return $wpdb->get_results("SELECT * FROM {$wpdb->posts} WHERE post_author = $user_id");
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Prepared Statement Placeholders
|
|
183
|
+
| Placeholder | Type |
|
|
184
|
+
|-------------|------|
|
|
185
|
+
| `%d` | Integer |
|
|
186
|
+
| `%f` | Float |
|
|
187
|
+
| `%s` | String |
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## Security Function Reference
|
|
192
|
+
|
|
193
|
+
### Quick Lookup Table
|
|
194
|
+
|
|
195
|
+
| Input Type | Sanitize | Escape (Output) |
|
|
196
|
+
|------------|----------|-----------------|
|
|
197
|
+
| Plain text | `sanitize_text_field()` | `esc_html()` |
|
|
198
|
+
| HTML content | `wp_kses_post()` | Already safe |
|
|
199
|
+
| Email | `sanitize_email()` | `esc_html()` |
|
|
200
|
+
| URL | `esc_url_raw()` | `esc_url()` |
|
|
201
|
+
| Integer | `absint()` | `absint()` |
|
|
202
|
+
| Filename | `sanitize_file_name()` | `esc_html()` |
|
|
203
|
+
| SQL | `$wpdb->prepare()` | N/A |
|