@silverassist/agents-toolkit 2.0.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 +135 -0
- package/README.md +388 -0
- package/bin/cli.js +940 -0
- package/package.json +52 -0
- package/src/index.js +58 -0
- package/templates/agents/AGENTS.codex.md +195 -0
- package/templates/agents/AGENTS.md +195 -0
- package/templates/agents/CLAUDE.md +213 -0
- package/templates/agents/copilot-instructions.md +83 -0
- package/templates/shared/instructions/css-styling.instructions.md +142 -0
- package/templates/shared/instructions/documentation-language.instructions.md +216 -0
- package/templates/shared/instructions/github-workflow.instructions.md +245 -0
- package/templates/shared/instructions/php-standards.instructions.md +293 -0
- package/templates/shared/instructions/react-components.instructions.md +196 -0
- package/templates/shared/instructions/server-actions.instructions.md +429 -0
- package/templates/shared/instructions/testing-standards.instructions.md +264 -0
- package/templates/shared/instructions/tests.instructions.md +103 -0
- package/templates/shared/instructions/typescript.instructions.md +106 -0
- package/templates/shared/instructions/wordpress-plugin-architecture.instructions.md +238 -0
- package/templates/shared/prompts/README.md +129 -0
- package/templates/shared/prompts/_partials/README.md +57 -0
- package/templates/shared/prompts/_partials/documentation.md +203 -0
- package/templates/shared/prompts/_partials/git-operations.md +171 -0
- package/templates/shared/prompts/_partials/github-integration.md +100 -0
- package/templates/shared/prompts/_partials/jira-integration.md +155 -0
- package/templates/shared/prompts/_partials/pr-template.md +216 -0
- package/templates/shared/prompts/_partials/validations.md +87 -0
- package/templates/shared/prompts/add-tests.prompt.md +151 -0
- package/templates/shared/prompts/analyze-github-issue.prompt.md +73 -0
- package/templates/shared/prompts/analyze-ticket.prompt.md +63 -0
- package/templates/shared/prompts/create-plan.prompt.md +118 -0
- package/templates/shared/prompts/create-pr.prompt.md +139 -0
- package/templates/shared/prompts/finalize-pr.prompt.md +150 -0
- package/templates/shared/prompts/fix-issues.prompt.md +92 -0
- package/templates/shared/prompts/new-wp-component.prompt.md +51 -0
- package/templates/shared/prompts/new-wp-plugin.prompt.md +46 -0
- package/templates/shared/prompts/prepare-pr.prompt.md +123 -0
- package/templates/shared/prompts/prepare-release.prompt.md +74 -0
- package/templates/shared/prompts/quality-check.prompt.md +45 -0
- package/templates/shared/prompts/review-code.prompt.md +81 -0
- package/templates/shared/prompts/work-github-issue.prompt.md +96 -0
- package/templates/shared/prompts/work-ticket.prompt.md +98 -0
- package/templates/shared/skills/README.md +53 -0
- package/templates/shared/skills/component-architecture/SKILL.md +321 -0
- package/templates/shared/skills/create-component/SKILL.md +714 -0
- package/templates/shared/skills/domain-driven-design/SKILL.md +277 -0
- package/templates/shared/skills/plugin-creation/SKILL.md +1094 -0
- package/templates/shared/skills/quality-checks/SKILL.md +493 -0
- package/templates/shared/skills/release-management/SKILL.md +649 -0
- package/templates/shared/skills/testing/SKILL.md +682 -0
- package/templates/shared/skills/testing-patterns/SKILL.md +243 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: GitHub workflow standards including branch management, PRs, issues, releases, and gh CLI usage for Silver Assist plugins
|
|
3
|
+
name: GitHub Workflow
|
|
4
|
+
applyTo: "**"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# GitHub Workflow — Silver Assist Plugins
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Branch Management
|
|
12
|
+
|
|
13
|
+
### Naming Conventions
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
main → Production branch (default)
|
|
17
|
+
feature/description → New features
|
|
18
|
+
fix/description → Bug fixes
|
|
19
|
+
chore/description → Maintenance tasks
|
|
20
|
+
hotfix/description → Emergency fixes
|
|
21
|
+
release/vX.Y.Z → Release preparation
|
|
22
|
+
refactor/description → Code refactoring
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Workflow
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# 1. Start new feature from main.
|
|
29
|
+
git checkout main && git pull origin main
|
|
30
|
+
git checkout -b feature/new-feature
|
|
31
|
+
|
|
32
|
+
# 2. Work and commit.
|
|
33
|
+
git add . && git commit -m "feat: Add new feature"
|
|
34
|
+
|
|
35
|
+
# 3. MANDATORY: Run quality checks BEFORE pushing.
|
|
36
|
+
vendor/bin/phpcs && vendor/bin/phpstan analyse && vendor/bin/phpunit
|
|
37
|
+
|
|
38
|
+
# 4. Push and create PR.
|
|
39
|
+
git push -u origin feature/new-feature
|
|
40
|
+
gh pr create --title "feat: Add new feature" --base main | cat
|
|
41
|
+
|
|
42
|
+
# 5. After merge, clean up branches.
|
|
43
|
+
git checkout main && git pull origin main
|
|
44
|
+
git branch -d feature/new-feature
|
|
45
|
+
git push origin --delete feature/new-feature
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Rules
|
|
49
|
+
|
|
50
|
+
- **NEVER** commit directly to `main`
|
|
51
|
+
- **NEVER** merge without CI passing
|
|
52
|
+
- **ALWAYS** delete branches after merge
|
|
53
|
+
- **ALWAYS** run quality checks before pushing
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## GitHub CLI — ALWAYS Pipe to `cat`
|
|
58
|
+
|
|
59
|
+
**CRITICAL**: Always append `| cat` or use `GH_PAGER=cat` to prevent terminal pagination:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# ✅ CORRECT
|
|
63
|
+
gh issue list | cat
|
|
64
|
+
gh pr view 42 | cat
|
|
65
|
+
GH_PAGER=cat gh run list --limit 5
|
|
66
|
+
|
|
67
|
+
# ❌ WRONG — Will hang waiting for pager input.
|
|
68
|
+
gh issue list
|
|
69
|
+
gh pr view 42
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Quick Reference
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Issues.
|
|
76
|
+
gh issue list | cat
|
|
77
|
+
gh issue create --label "enhancement" | cat
|
|
78
|
+
gh issue view NUMBER | cat
|
|
79
|
+
gh issue close NUMBER --comment "Completed in PR #XX" | cat
|
|
80
|
+
|
|
81
|
+
# Pull Requests.
|
|
82
|
+
gh pr list | cat
|
|
83
|
+
gh pr create --base main | cat
|
|
84
|
+
gh pr view NUMBER | cat
|
|
85
|
+
gh pr checks NUMBER | cat
|
|
86
|
+
gh pr merge NUMBER --squash --delete-branch | cat
|
|
87
|
+
|
|
88
|
+
# Releases.
|
|
89
|
+
gh release list | cat
|
|
90
|
+
gh release view TAG | cat
|
|
91
|
+
|
|
92
|
+
# Workflows.
|
|
93
|
+
GH_PAGER=cat gh run list --limit 5
|
|
94
|
+
GH_PAGER=cat gh workflow list
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Issue Management
|
|
100
|
+
|
|
101
|
+
### Required Labels
|
|
102
|
+
|
|
103
|
+
**Type**: `enhancement`, `bug`, `documentation`, `refactoring`, `testing`, `security`, `performance`
|
|
104
|
+
|
|
105
|
+
**Priority**: `major-release` (X.0.0), `minor-release` (1.X.0), `patch-release` (1.3.X)
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## PR Reviews vs Comments (CRITICAL)
|
|
110
|
+
|
|
111
|
+
| Type | Location | How to Reply |
|
|
112
|
+
|------|----------|--------------|
|
|
113
|
+
| **Reviews** | "Files changed" tab, inline on code | `gh api graphql` mutation |
|
|
114
|
+
| **Comments** | "Conversation" tab | `gh pr comment` or MCP |
|
|
115
|
+
|
|
116
|
+
### Replying to PR Review Threads
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
# 1. Get review threads.
|
|
120
|
+
gh api graphql -f query='
|
|
121
|
+
query {
|
|
122
|
+
repository(owner: "SilverAssist", name: "REPO_NAME") {
|
|
123
|
+
pullRequest(number: PR_NUMBER) {
|
|
124
|
+
reviewThreads(first: 50) {
|
|
125
|
+
nodes {
|
|
126
|
+
id
|
|
127
|
+
path
|
|
128
|
+
isResolved
|
|
129
|
+
comments(first: 5) { nodes { body author { login } } }
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}' | cat
|
|
135
|
+
|
|
136
|
+
# 2. Reply to thread (SEPARATE mutation per thread).
|
|
137
|
+
gh api graphql -f query='
|
|
138
|
+
mutation {
|
|
139
|
+
addPullRequestReviewThreadReply(input: {
|
|
140
|
+
pullRequestReviewThreadId: "PRRT_kwDOXXXXX",
|
|
141
|
+
body: "Applied in commit [SHA](url). **Description.**"
|
|
142
|
+
}) { comment { id } }
|
|
143
|
+
}' | cat
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Semantic Versioning
|
|
149
|
+
|
|
150
|
+
| Version | Type | When to Use |
|
|
151
|
+
|---------|------|-------------|
|
|
152
|
+
| MAJOR (X.0.0) | Breaking changes | Architecture refactoring |
|
|
153
|
+
| MINOR (1.X.0) | New features | Backwards compatible additions |
|
|
154
|
+
| PATCH (1.3.X) | Bug fixes | Documentation, small fixes |
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Post-PR Merge Checklist
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
# 1. Clean up branches.
|
|
162
|
+
git checkout main && git pull origin main
|
|
163
|
+
git branch -d feature/your-feature
|
|
164
|
+
git push origin --delete feature/your-feature
|
|
165
|
+
git fetch --prune
|
|
166
|
+
|
|
167
|
+
# 2. Close related issues.
|
|
168
|
+
gh issue close ISSUE_NUMBER --comment "✅ Completed in PR #XX" | cat
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## GitHub Actions Best Practices
|
|
174
|
+
|
|
175
|
+
### Dependabot Auto-merge Workflow
|
|
176
|
+
|
|
177
|
+
When creating jobs that only run for Dependabot PRs, **NEVER** use job-level `if: github.actor == 'dependabot[bot]'`. This causes the job to be "skipped", which can:
|
|
178
|
+
- Fail branch protection rules that require the job to pass
|
|
179
|
+
- Show confusing status in the PR checks
|
|
180
|
+
|
|
181
|
+
**❌ WRONG — Job skipped for non-Dependabot PRs:**
|
|
182
|
+
```yaml
|
|
183
|
+
jobs:
|
|
184
|
+
auto-merge-dependabot:
|
|
185
|
+
runs-on: ubuntu-latest
|
|
186
|
+
if: github.actor == 'dependabot[bot]' # ❌ Job will be SKIPPED
|
|
187
|
+
steps:
|
|
188
|
+
- name: Dependabot metadata
|
|
189
|
+
uses: dependabot/fetch-metadata@v2
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**✅ CORRECT — Job runs, steps conditionally execute:**
|
|
193
|
+
```yaml
|
|
194
|
+
jobs:
|
|
195
|
+
auto-merge-dependabot:
|
|
196
|
+
runs-on: ubuntu-latest
|
|
197
|
+
if: github.event_name == 'pull_request' && success() # ✅ Always runs for PRs
|
|
198
|
+
steps:
|
|
199
|
+
- name: Check if Dependabot PR
|
|
200
|
+
id: check-actor
|
|
201
|
+
run: |
|
|
202
|
+
if [ "${{ github.actor }}" = "dependabot[bot]" ]; then
|
|
203
|
+
echo "is_dependabot=true" >> $GITHUB_OUTPUT
|
|
204
|
+
echo "✅ This is a Dependabot PR, proceeding with auto-merge checks."
|
|
205
|
+
else
|
|
206
|
+
echo "is_dependabot=false" >> $GITHUB_OUTPUT
|
|
207
|
+
echo "ℹ️ Not a Dependabot PR, skipping auto-merge steps."
|
|
208
|
+
fi
|
|
209
|
+
|
|
210
|
+
- name: Dependabot metadata
|
|
211
|
+
if: steps.check-actor.outputs.is_dependabot == 'true'
|
|
212
|
+
id: metadata
|
|
213
|
+
uses: dependabot/fetch-metadata@v2
|
|
214
|
+
|
|
215
|
+
- name: Auto-approve PR
|
|
216
|
+
if: |
|
|
217
|
+
steps.check-actor.outputs.is_dependabot == 'true' && (
|
|
218
|
+
steps.metadata.outputs.update-type == 'version-update:semver-patch' ||
|
|
219
|
+
steps.metadata.outputs.update-type == 'version-update:semver-minor'
|
|
220
|
+
)
|
|
221
|
+
run: gh pr review --approve "$PR_URL"
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Action Version Pinning
|
|
225
|
+
|
|
226
|
+
**ALWAYS** pin GitHub Actions to full commit SHA with version comment:
|
|
227
|
+
|
|
228
|
+
```yaml
|
|
229
|
+
# ✅ CORRECT — SHA pinned with version comment.
|
|
230
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
231
|
+
|
|
232
|
+
# ❌ WRONG — Floating tag, insecure.
|
|
233
|
+
uses: actions/checkout@v4
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Standard Workflow Files
|
|
237
|
+
|
|
238
|
+
Every Silver Assist plugin should have these workflows in `.github/workflows/`:
|
|
239
|
+
|
|
240
|
+
| File | Purpose |
|
|
241
|
+
|------|---------|
|
|
242
|
+
| `quality-checks.yml` | PHPCS, PHPStan, PHPUnit on PRs and pushes |
|
|
243
|
+
| `release.yml` | Automated releases on tag push |
|
|
244
|
+
| `dependency-updates.yml` | Weekly dependency checks + Dependabot auto-merge |
|
|
245
|
+
| `copilot-setup-steps.yml` | Reusable setup for Copilot Coding Agent |
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: PHP coding standards, WordPress conventions, security, type safety, and i18n for Silver Assist WordPress plugins
|
|
3
|
+
name: PHP Standards
|
|
4
|
+
applyTo: "**/*.php"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# PHP Code Quality Standards — Silver Assist Plugins
|
|
8
|
+
|
|
9
|
+
**Applies to**: All PHP files in Silver Assist WordPress plugins
|
|
10
|
+
**Standards**: PHPCS (WordPress-Extra), PHPStan Level 8, PHP 8.2+
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Quality Gates (MANDATORY Before Commit)
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# 1. Auto-fix formatting (REQUIRED FIRST).
|
|
18
|
+
vendor/bin/phpcbf
|
|
19
|
+
|
|
20
|
+
# 2. Check WordPress Coding Standards (MUST PASS — 0 errors).
|
|
21
|
+
vendor/bin/phpcs
|
|
22
|
+
|
|
23
|
+
# 3. Static Analysis Level 8 (MUST PASS — 0 errors).
|
|
24
|
+
vendor/bin/phpstan analyse includes/ --level=8
|
|
25
|
+
|
|
26
|
+
# 4. Run tests (MUST PASS — all green).
|
|
27
|
+
vendor/bin/phpunit
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Zero tolerance**: Code with PHPCS or PHPStan errors will be rejected by CI/CD.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## WordPress Coding Standards (PHPCS)
|
|
35
|
+
|
|
36
|
+
### 1. Inline Comments MUST End with Punctuation
|
|
37
|
+
|
|
38
|
+
```php
|
|
39
|
+
// ✅ CORRECT
|
|
40
|
+
// This is a proper comment.
|
|
41
|
+
the_content();
|
|
42
|
+
|
|
43
|
+
// ❌ WRONG — Will fail PHPCS.
|
|
44
|
+
// This is wrong
|
|
45
|
+
the_content();
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 2. Use Tabs for Indentation (WordPress Standard)
|
|
49
|
+
|
|
50
|
+
```php
|
|
51
|
+
// ✅ CORRECT
|
|
52
|
+
if ( $condition ) {
|
|
53
|
+
echo 'Use tabs';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ❌ WRONG
|
|
57
|
+
if ( $condition ) {
|
|
58
|
+
echo 'Spaces fail PHPCS';
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 3. Spaces Inside Parentheses
|
|
63
|
+
|
|
64
|
+
```php
|
|
65
|
+
// ✅ CORRECT — Spaces inside parentheses.
|
|
66
|
+
if ( $condition ) {
|
|
67
|
+
do_something( $param1, $param2 );
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ❌ WRONG — No spaces.
|
|
71
|
+
if ($condition) {
|
|
72
|
+
do_something($param1, $param2);
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 4. String Quotation
|
|
77
|
+
|
|
78
|
+
```php
|
|
79
|
+
// ✅ Single quotes for strings without interpolation.
|
|
80
|
+
$status = 'error';
|
|
81
|
+
__( 'Text', 'plugin-text-domain' );
|
|
82
|
+
|
|
83
|
+
// ✅ Double quotes for interpolation.
|
|
84
|
+
$message = "Error in form {$form_id}";
|
|
85
|
+
|
|
86
|
+
// ❌ WRONG — Double quotes without interpolation.
|
|
87
|
+
$status = "error";
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 5. WordPress Functions in Namespaced Code
|
|
91
|
+
|
|
92
|
+
```php
|
|
93
|
+
// ✅ Use backslash prefix for WordPress/global functions.
|
|
94
|
+
\add_action( 'init', [ $this, 'init' ] );
|
|
95
|
+
\get_option( 'option_name' );
|
|
96
|
+
\sanitize_text_field( $input );
|
|
97
|
+
|
|
98
|
+
// ❌ No prefix causes potential namespace resolution issues.
|
|
99
|
+
add_action( 'init', [ $this, 'init' ] );
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### 6. PHP File Header
|
|
103
|
+
|
|
104
|
+
```php
|
|
105
|
+
<?php
|
|
106
|
+
/**
|
|
107
|
+
* File description.
|
|
108
|
+
*
|
|
109
|
+
* @package SilverAssist\PluginName
|
|
110
|
+
* @subpackage Component
|
|
111
|
+
* @since X.Y.Z
|
|
112
|
+
*/
|
|
113
|
+
|
|
114
|
+
namespace SilverAssist\PluginName\Component;
|
|
115
|
+
|
|
116
|
+
\defined( 'ABSPATH' ) || exit;
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Security (CRITICAL)
|
|
122
|
+
|
|
123
|
+
### Nonce Validation (CSRF Protection)
|
|
124
|
+
|
|
125
|
+
```php
|
|
126
|
+
// ✅ CORRECT — Verify nonce FIRST in AJAX handlers.
|
|
127
|
+
public function ajax_handler(): void {
|
|
128
|
+
check_ajax_referer( 'plugin_nonce', 'nonce' );
|
|
129
|
+
|
|
130
|
+
if ( ! current_user_can( 'manage_options' ) ) {
|
|
131
|
+
wp_send_json_error( 'Unauthorized', 403 );
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
wp_send_json_success( $data );
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Input Sanitization
|
|
140
|
+
|
|
141
|
+
```php
|
|
142
|
+
$text = \sanitize_text_field( \wp_unslash( $_POST['field'] ) );
|
|
143
|
+
$email = \sanitize_email( $_POST['email'] );
|
|
144
|
+
$url = \esc_url_raw( $_POST['url'] );
|
|
145
|
+
$int = \absint( $_POST['number'] );
|
|
146
|
+
$key = \sanitize_key( $_POST['key'] );
|
|
147
|
+
$array = \array_map( 'sanitize_text_field', $_POST['items'] );
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Output Escaping
|
|
151
|
+
|
|
152
|
+
| Function | Use Case | Example |
|
|
153
|
+
|----------|----------|---------|
|
|
154
|
+
| `esc_html()` | Plain text | `<h1><?php echo esc_html( $title ); ?></h1>` |
|
|
155
|
+
| `esc_url()` | URLs (href, src) | `<a href="<?php echo esc_url( $url ); ?>">` |
|
|
156
|
+
| `esc_attr()` | HTML attributes | `<div class="<?php echo esc_attr( $class ); ?>">` |
|
|
157
|
+
| `wp_kses_post()` | Rich HTML content | `<?php echo wp_kses_post( $content ); ?>` |
|
|
158
|
+
|
|
159
|
+
### Database Queries — Always Prepared Statements
|
|
160
|
+
|
|
161
|
+
```php
|
|
162
|
+
// ✅ CORRECT — Use %i for table/column names (WordPress 6.2+).
|
|
163
|
+
$results = $wpdb->get_results(
|
|
164
|
+
$wpdb->prepare(
|
|
165
|
+
'SELECT * FROM %i WHERE status = %s AND form_id = %d',
|
|
166
|
+
$table_name,
|
|
167
|
+
$status,
|
|
168
|
+
$form_id
|
|
169
|
+
),
|
|
170
|
+
ARRAY_A
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
// ❌ WRONG — Direct interpolation (SQL injection risk).
|
|
174
|
+
$results = $wpdb->get_results(
|
|
175
|
+
"SELECT * FROM {$table_name} WHERE status = '{$status}'"
|
|
176
|
+
);
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**Placeholder Reference:**
|
|
180
|
+
|
|
181
|
+
| Placeholder | Use Case |
|
|
182
|
+
|-------------|----------|
|
|
183
|
+
| `%i` | Identifiers (table/column names) |
|
|
184
|
+
| `%s` | Strings |
|
|
185
|
+
| `%d` | Integers |
|
|
186
|
+
| `%f` | Floats |
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Type Safety (PHPStan Level 8)
|
|
191
|
+
|
|
192
|
+
### Strict Types Required
|
|
193
|
+
|
|
194
|
+
```php
|
|
195
|
+
// ✅ Full type hints on all methods.
|
|
196
|
+
public function process_log( int $log_id, string $status ): bool {
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* @param array<string, mixed> $filters Filter parameters.
|
|
202
|
+
* @return array<int, array<string, mixed>> Log entries.
|
|
203
|
+
*/
|
|
204
|
+
public function get_logs( array $filters ): array {
|
|
205
|
+
return [];
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Nullable Types
|
|
210
|
+
|
|
211
|
+
```php
|
|
212
|
+
private ?int $log_id = null;
|
|
213
|
+
|
|
214
|
+
public function get_log( int $log_id ): ?array {
|
|
215
|
+
// May return null if not found.
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### PHPDoc for Complex Types
|
|
220
|
+
|
|
221
|
+
```php
|
|
222
|
+
/**
|
|
223
|
+
* @param array{
|
|
224
|
+
* id: int,
|
|
225
|
+
* status: string,
|
|
226
|
+
* error_message?: string,
|
|
227
|
+
* } $entry Entry data.
|
|
228
|
+
* @return array{items: array<int, array<string, mixed>>, total: int}
|
|
229
|
+
*/
|
|
230
|
+
public function process_entry( array $entry ): array {
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## Internationalization (i18n)
|
|
237
|
+
|
|
238
|
+
### Text Domain — Always Literal String
|
|
239
|
+
|
|
240
|
+
```php
|
|
241
|
+
// ✅ CORRECT — Literal text domain (extractable).
|
|
242
|
+
__( 'Error occurred', 'plugin-text-domain' );
|
|
243
|
+
esc_html_e( 'Success!', 'plugin-text-domain' );
|
|
244
|
+
|
|
245
|
+
// ❌ WRONG — Variable/constant (NOT extractable by wp i18n make-pot).
|
|
246
|
+
__( 'Error', $text_domain );
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Ordered Placeholders (MANDATORY for Multiple Args)
|
|
250
|
+
|
|
251
|
+
```php
|
|
252
|
+
// ✅ CORRECT — Positional placeholders with translator comment.
|
|
253
|
+
sprintf(
|
|
254
|
+
/* translators: %1$s: form name, %2$d: submission count */
|
|
255
|
+
__( 'Form "%1$s" has %2$d submissions', 'plugin-text-domain' ),
|
|
256
|
+
$form_name,
|
|
257
|
+
$count
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
// ❌ WRONG — Unordered placeholders (PHPCS error).
|
|
261
|
+
sprintf( __( 'Form "%s" has %d submissions', 'plugin-text-domain' ), $form_name, $count );
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Translation Functions Reference
|
|
265
|
+
|
|
266
|
+
| Function | Use Case |
|
|
267
|
+
|----------|----------|
|
|
268
|
+
| `__()` | Return translated string |
|
|
269
|
+
| `_e()` | Echo translated string |
|
|
270
|
+
| `esc_html__()` | Return translated + escaped for HTML |
|
|
271
|
+
| `esc_html_e()` | Echo translated + escaped for HTML |
|
|
272
|
+
| `esc_attr__()` | Return translated + escaped for attribute |
|
|
273
|
+
| `_n()` | Singular/plural forms |
|
|
274
|
+
| `_x()` | Translation with context |
|
|
275
|
+
|
|
276
|
+
### Pre-PR: Update .pot File
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
wp i18n make-pot . languages/plugin-text-domain.pot --domain=plugin-text-domain
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## Common PHPCS Errors & Quick Fixes
|
|
285
|
+
|
|
286
|
+
| Error | Fix |
|
|
287
|
+
|-------|-----|
|
|
288
|
+
| `Inline comments must end in full-stops` | Add `.` to end of comment |
|
|
289
|
+
| `Whitespace found at end of line` | Remove trailing spaces (phpcbf auto-fixes) |
|
|
290
|
+
| `Tabs must be used to indent lines` | Replace spaces with tabs |
|
|
291
|
+
| `Use single quotes when not evaluating` | Change `"string"` to `'string'` |
|
|
292
|
+
| `Detected usage of a non-sanitized input` | Add `sanitize_*()` function |
|
|
293
|
+
| `OutputNotEscaped` | Use `esc_html()`, `esc_url()`, `esc_attr()` |
|