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,403 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
declare(strict_types=1);
|
|
3
|
+
|
|
4
|
+
namespace PhpFp\Examples;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Pure function examples demonstrating PHP FP patterns
|
|
8
|
+
*
|
|
9
|
+
* Key principles:
|
|
10
|
+
* - Strict types enabled
|
|
11
|
+
* - Pure functions (no side effects, deterministic)
|
|
12
|
+
* - Native PHP array functions over loops
|
|
13
|
+
* - Explicit dependencies via parameters
|
|
14
|
+
* - Immutable data handling
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// ───────────────────────────────────────────────────────
|
|
18
|
+
// Basic Pure Functions
|
|
19
|
+
// ───────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Calculate user discount based on tier and loyalty
|
|
23
|
+
* @pure - No side effects, deterministic
|
|
24
|
+
*/
|
|
25
|
+
function calculateDiscount(float $price, string $tier, int $daysAsMember): float
|
|
26
|
+
{
|
|
27
|
+
$tierMultipliers = [
|
|
28
|
+
'bronze' => 0.05,
|
|
29
|
+
'silver' => 0.10,
|
|
30
|
+
'gold' => 0.15,
|
|
31
|
+
'platinum' => 0.20
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
$baseDiscount = $tierMultipliers[$tier] ?? 0.0;
|
|
35
|
+
$loyaltyBonus = min($daysAsMember / 365 * 0.02, 0.05); // Max 5%
|
|
36
|
+
$totalDiscount = min($baseDiscount + $loyaltyBonus, 0.25); // Max 25%
|
|
37
|
+
|
|
38
|
+
return round($price * (1 - $totalDiscount), 2);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Validate email format
|
|
43
|
+
* @pure - Returns validation result without side effects
|
|
44
|
+
*/
|
|
45
|
+
function validateEmail(string $email): array
|
|
46
|
+
{
|
|
47
|
+
$isValid = filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
|
|
48
|
+
|
|
49
|
+
return [
|
|
50
|
+
'valid' => $isValid,
|
|
51
|
+
'error' => $isValid ? null : 'Invalid email format'
|
|
52
|
+
];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Format user display name
|
|
57
|
+
* @pure - String transformation only
|
|
58
|
+
*/
|
|
59
|
+
function formatDisplayName(array $user): string
|
|
60
|
+
{
|
|
61
|
+
$firstName = trim($user['first_name'] ?? '');
|
|
62
|
+
$lastName = trim($user['last_name'] ?? '');
|
|
63
|
+
|
|
64
|
+
if ($firstName && $lastName) {
|
|
65
|
+
return "{$firstName} {$lastName}";
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return $firstName ?: $lastName ?: 'Anonymous';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ───────────────────────────────────────────────────────
|
|
72
|
+
// Configuration Pre-Compilation (Performance Pattern)
|
|
73
|
+
// ───────────────────────────────────────────────────────
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Pre-compile validation rules to avoid O(n²) complexity
|
|
77
|
+
*
|
|
78
|
+
* ❌ BAD: Configuration inside loop
|
|
79
|
+
* foreach ($users as $user) {
|
|
80
|
+
* $rules = ['email' => 'required', 'age' => 'numeric']; // Created N times!
|
|
81
|
+
* validate($user, $rules);
|
|
82
|
+
* }
|
|
83
|
+
*
|
|
84
|
+
* ✅ GOOD: Pre-compile configuration
|
|
85
|
+
* $validator = createValidator(['email' => 'required', 'age' => 'numeric']);
|
|
86
|
+
* foreach ($users as $user) {
|
|
87
|
+
* $validator($user); // Uses pre-compiled rules
|
|
88
|
+
* }
|
|
89
|
+
*/
|
|
90
|
+
function createValidator(array $rules): callable
|
|
91
|
+
{
|
|
92
|
+
// Pre-compile rules once (expensive operation)
|
|
93
|
+
$compiledRules = array_map(function($rule) {
|
|
94
|
+
return match($rule) {
|
|
95
|
+
'required' => fn($value) => !empty($value),
|
|
96
|
+
'numeric' => fn($value) => is_numeric($value),
|
|
97
|
+
'email' => fn($value) => filter_var($value, FILTER_VALIDATE_EMAIL) !== false,
|
|
98
|
+
default => fn($value) => true
|
|
99
|
+
};
|
|
100
|
+
}, $rules);
|
|
101
|
+
|
|
102
|
+
// Return validator function that uses pre-compiled rules
|
|
103
|
+
return function(array $data) use ($compiledRules): array {
|
|
104
|
+
$errors = [];
|
|
105
|
+
|
|
106
|
+
foreach ($compiledRules as $field => $validator) {
|
|
107
|
+
$value = $data[$field] ?? null;
|
|
108
|
+
if (!$validator($value)) {
|
|
109
|
+
$errors[$field] = "Validation failed for {$field}";
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return [
|
|
114
|
+
'valid' => empty($errors),
|
|
115
|
+
'errors' => $errors
|
|
116
|
+
];
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Pre-compile field transformer configuration
|
|
122
|
+
*/
|
|
123
|
+
function createFieldTransformer(array $fieldConfig): callable
|
|
124
|
+
{
|
|
125
|
+
// Pre-compile transformation functions
|
|
126
|
+
$transformers = array_map(function($transform) {
|
|
127
|
+
return match($transform) {
|
|
128
|
+
'trim' => fn($v) => trim($v),
|
|
129
|
+
'lowercase' => fn($v) => strtolower($v),
|
|
130
|
+
'uppercase' => fn($v) => strtoupper($v),
|
|
131
|
+
'capitalize' => fn($v) => ucfirst($v),
|
|
132
|
+
default => fn($v) => $v
|
|
133
|
+
};
|
|
134
|
+
}, $fieldConfig);
|
|
135
|
+
|
|
136
|
+
return function(array $data) use ($transformers): array {
|
|
137
|
+
$transformed = $data;
|
|
138
|
+
|
|
139
|
+
foreach ($transformers as $field => $transform) {
|
|
140
|
+
if (isset($transformed[$field])) {
|
|
141
|
+
$transformed[$field] = $transform($transformed[$field]);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return $transformed;
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ───────────────────────────────────────────────────────
|
|
150
|
+
// Composition with Native PHP Functions
|
|
151
|
+
// ───────────────────────────────────────────────────────
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Process users with native array functions
|
|
155
|
+
* Demonstrates composition without pipe/compose utilities
|
|
156
|
+
*/
|
|
157
|
+
function processActiveUsers(array $users, int $limit = 10): array
|
|
158
|
+
{
|
|
159
|
+
return array_slice(
|
|
160
|
+
array_map(
|
|
161
|
+
fn($user) => [
|
|
162
|
+
...$user,
|
|
163
|
+
'display_name' => formatDisplayName($user),
|
|
164
|
+
'account_age' => calculateAccountAge($user['created_at'])
|
|
165
|
+
],
|
|
166
|
+
array_filter(
|
|
167
|
+
$users,
|
|
168
|
+
fn($user) => $user['active'] ?? false
|
|
169
|
+
)
|
|
170
|
+
),
|
|
171
|
+
0,
|
|
172
|
+
$limit
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Calculate account age in days
|
|
178
|
+
* @pure - Date calculation only
|
|
179
|
+
*/
|
|
180
|
+
function calculateAccountAge(string $createdAt): int
|
|
181
|
+
{
|
|
182
|
+
$created = strtotime($createdAt);
|
|
183
|
+
$now = time();
|
|
184
|
+
|
|
185
|
+
return (int) (($now - $created) / (60 * 60 * 24));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Group items by field value
|
|
190
|
+
* @pure - Array transformation
|
|
191
|
+
*/
|
|
192
|
+
function groupBy(array $items, string $field): array
|
|
193
|
+
{
|
|
194
|
+
return array_reduce(
|
|
195
|
+
$items,
|
|
196
|
+
function($groups, $item) use ($field) {
|
|
197
|
+
$key = $item[$field] ?? 'unknown';
|
|
198
|
+
$groups[$key][] = $item;
|
|
199
|
+
return $groups;
|
|
200
|
+
},
|
|
201
|
+
[]
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// ───────────────────────────────────────────────────────
|
|
206
|
+
// Immutability Patterns (PHP-Specific)
|
|
207
|
+
// ───────────────────────────────────────────────────────
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Update user without mutation
|
|
211
|
+
* PHP arrays are copy-on-write, but we make it explicit
|
|
212
|
+
*/
|
|
213
|
+
function updateUser(array $user, array $updates): array
|
|
214
|
+
{
|
|
215
|
+
// Create new array instead of mutating
|
|
216
|
+
return array_merge($user, $updates, [
|
|
217
|
+
'updated_at' => date('Y-m-d H:i:s')
|
|
218
|
+
]);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Add item to collection immutably
|
|
223
|
+
*/
|
|
224
|
+
function addItem(array $collection, array $item): array
|
|
225
|
+
{
|
|
226
|
+
// array_merge creates new array
|
|
227
|
+
return array_merge($collection, [$item]);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Remove item from collection immutably
|
|
232
|
+
*/
|
|
233
|
+
function removeItem(array $collection, string $id): array
|
|
234
|
+
{
|
|
235
|
+
// array_filter creates new array
|
|
236
|
+
return array_values(
|
|
237
|
+
array_filter(
|
|
238
|
+
$collection,
|
|
239
|
+
fn($item) => $item['id'] !== $id
|
|
240
|
+
)
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// ───────────────────────────────────────────────────────
|
|
245
|
+
// Result Type Pattern (Error Handling)
|
|
246
|
+
// ───────────────────────────────────────────────────────
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Parse and validate user data
|
|
250
|
+
* @pure - Returns result, throws no exceptions
|
|
251
|
+
*/
|
|
252
|
+
function parseUserData(array $data): array
|
|
253
|
+
{
|
|
254
|
+
// Validate required fields
|
|
255
|
+
$requiredFields = ['email', 'name'];
|
|
256
|
+
$missingFields = array_filter(
|
|
257
|
+
$requiredFields,
|
|
258
|
+
fn($field) => empty($data[$field])
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
if (!empty($missingFields)) {
|
|
262
|
+
return [
|
|
263
|
+
'success' => false,
|
|
264
|
+
'error' => 'Missing required fields: ' . implode(', ', $missingFields),
|
|
265
|
+
'data' => null
|
|
266
|
+
];
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Validate email
|
|
270
|
+
$emailValidation = validateEmail($data['email']);
|
|
271
|
+
if (!$emailValidation['valid']) {
|
|
272
|
+
return [
|
|
273
|
+
'success' => false,
|
|
274
|
+
'error' => $emailValidation['error'],
|
|
275
|
+
'data' => null
|
|
276
|
+
];
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Success - return parsed data
|
|
280
|
+
return [
|
|
281
|
+
'success' => true,
|
|
282
|
+
'error' => null,
|
|
283
|
+
'data' => [
|
|
284
|
+
'email' => strtolower(trim($data['email'])),
|
|
285
|
+
'name' => trim($data['name']),
|
|
286
|
+
'age' => isset($data['age']) ? (int) $data['age'] : null
|
|
287
|
+
]
|
|
288
|
+
];
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Calculate shipping cost with error handling
|
|
293
|
+
* @pure - Returns result type
|
|
294
|
+
*/
|
|
295
|
+
function calculateShipping(float $weight, string $zone): array
|
|
296
|
+
{
|
|
297
|
+
$validZones = ['domestic', 'international', 'express'];
|
|
298
|
+
|
|
299
|
+
if (!in_array($zone, $validZones)) {
|
|
300
|
+
return [
|
|
301
|
+
'success' => false,
|
|
302
|
+
'error' => "Invalid zone: {$zone}",
|
|
303
|
+
'data' => null
|
|
304
|
+
];
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if ($weight <= 0) {
|
|
308
|
+
return [
|
|
309
|
+
'success' => false,
|
|
310
|
+
'error' => 'Weight must be positive',
|
|
311
|
+
'data' => null
|
|
312
|
+
];
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
$rates = [
|
|
316
|
+
'domestic' => 5.00,
|
|
317
|
+
'international' => 15.00,
|
|
318
|
+
'express' => 25.00
|
|
319
|
+
];
|
|
320
|
+
|
|
321
|
+
$cost = $rates[$zone] + ($weight * 0.50);
|
|
322
|
+
|
|
323
|
+
return [
|
|
324
|
+
'success' => true,
|
|
325
|
+
'error' => null,
|
|
326
|
+
'data' => round($cost, 2)
|
|
327
|
+
];
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// ───────────────────────────────────────────────────────
|
|
331
|
+
// Dependency Injection Examples
|
|
332
|
+
// ───────────────────────────────────────────────────────
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Process order with injected dependencies
|
|
336
|
+
* @pure - All dependencies passed as parameters
|
|
337
|
+
*/
|
|
338
|
+
function processOrder(
|
|
339
|
+
array $order,
|
|
340
|
+
callable $calculateTax,
|
|
341
|
+
callable $calculateShipping,
|
|
342
|
+
callable $applyDiscount
|
|
343
|
+
): array {
|
|
344
|
+
$subtotal = array_sum(array_column($order['items'], 'price'));
|
|
345
|
+
$tax = $calculateTax($subtotal, $order['tax_zone']);
|
|
346
|
+
$shipping = $calculateShipping($order['weight'], $order['shipping_zone']);
|
|
347
|
+
$discount = $applyDiscount($subtotal, $order['discount_code'] ?? null);
|
|
348
|
+
|
|
349
|
+
return [
|
|
350
|
+
'subtotal' => $subtotal,
|
|
351
|
+
'tax' => $tax,
|
|
352
|
+
'shipping' => $shipping,
|
|
353
|
+
'discount' => $discount,
|
|
354
|
+
'total' => $subtotal + $tax + $shipping - $discount
|
|
355
|
+
];
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Calculate tax based on zone
|
|
360
|
+
* @pure - Tax calculation only
|
|
361
|
+
*/
|
|
362
|
+
function calculateTax(float $amount, string $zone): float
|
|
363
|
+
{
|
|
364
|
+
$rates = [
|
|
365
|
+
'CA' => 0.0725,
|
|
366
|
+
'NY' => 0.08875,
|
|
367
|
+
'TX' => 0.0625
|
|
368
|
+
];
|
|
369
|
+
|
|
370
|
+
$rate = $rates[$zone] ?? 0.0;
|
|
371
|
+
return round($amount * $rate, 2);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Calculate shipping cost
|
|
376
|
+
* @pure - Shipping calculation only
|
|
377
|
+
*/
|
|
378
|
+
function calculateShippingCost(float $weight, string $zone): float
|
|
379
|
+
{
|
|
380
|
+
$baseRates = [
|
|
381
|
+
'domestic' => 5.00,
|
|
382
|
+
'international' => 15.00
|
|
383
|
+
];
|
|
384
|
+
|
|
385
|
+
$base = $baseRates[$zone] ?? 10.00;
|
|
386
|
+
return round($base + ($weight * 0.50), 2);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Apply discount code
|
|
391
|
+
* @pure - Discount calculation only
|
|
392
|
+
*/
|
|
393
|
+
function applyDiscountCode(float $amount, ?string $code): float
|
|
394
|
+
{
|
|
395
|
+
$discounts = [
|
|
396
|
+
'SAVE10' => 0.10,
|
|
397
|
+
'SAVE20' => 0.20,
|
|
398
|
+
'SAVE30' => 0.30
|
|
399
|
+
];
|
|
400
|
+
|
|
401
|
+
$rate = $discounts[$code ?? ''] ?? 0.0;
|
|
402
|
+
return round($amount * $rate, 2);
|
|
403
|
+
}
|