# Skill: WordPress PHP Integration

Skill ID: `wordpress-php-integration`

Core rules
- PHP file header: `declare(strict_types=1);`, `namespace TranslatePresszone;`, ABSPATH guard.
- Text domain: `translate-press-zone` only.
- Text Domain must be exactly `'translate-press-zone'`.
- Prefix: `presszone_translate_` for functions/options/keys/constants/hooks.
- Capability checks: `current_user_can()`/`user_can()`; never `is_admin()`.
- Request pipeline: `wp_unslash()` then sanitize (`sanitize_text_field`, `sanitize_textarea_field`, `sanitize_key`, `absint`, `sanitize_email`, `esc_url_raw`).
- Output pipeline: `esc_html`, `esc_attr`, `esc_url`, `wp_kses_post`.
- Nonce: sanitize then `wp_verify_nonce()` for all state-changing actions.
- REST: strict `permission_callback`; never `__return_true` for privileged routes.
- SQL: dynamic statements use `$wpdb->prepare()`; LIKE uses `$wpdb->esc_like()`; dynamic order/state values use allowlists.
- Redirects: `wp_safe_redirect()` for user-influenced targets.
- File operations: validate paths with `validate_file()`.
- HTTP: `wp_safe_remote_get/post/request` ONLY — never use `wp_remote_*` without the `safe` prefix. The `wp_safe_*` variants prevent SSRF by blocking private/internal IPs. No cURL or `file_get_contents`.
- Hooks: register through WP hooks; avoid direct execution paths.
- No CDN assets.
- Bundle all fonts and assets locally (no CDNs).
- No inline CSS logic (`style=`/`wp_add_inline_style`).
- Do not expose sensitive backend/API internals in user-visible errors.
- Security logs include metadata only (no secrets).
- Handle API failures gracefully and return user-safe feedback.
- Never use hardcoded user IDs or emails in authorization/data filters.
- Verify nonces for all API-related form submissions.
- Log security-related events for auditing.
- NEVER use unconditional `error_log()` — all debug logging MUST be wrapped in `if (defined('WP_DEBUG') && WP_DEBUG)`. WordPress.org reviewers reject unconditional logging.
- Always `return` after `wp_send_json_error()` / `wp_send_json_success()` — execution continues otherwise.
- `wp_kses` allowlist must NEVER include `'style' => true` — this enables XSS via CSS injection (`expression()`, `url()`, `-moz-binding`).
- Health/diagnostic endpoints require `manage_options` capability, never just `is_user_logged_in`.
- `uninstall.php` must delete ALL plugin options, transients, scheduled events, and custom tables — cross-reference every `update_option()`/`add_option()` call in the codebase.
- Avoid hardcoded colors/animation durations in PHP-generated markup or config.
- Prefer SCSS variables over CSS custom-property shortcuts like `var(--pz-*)` in feature logic.
- `var(--pz-*)` patterns are not allowed for feature logic.
- Responsive design must support zoom up to 200% without horizontal scrolling.
- Never rely solely on color to convey information.
- Database tables should use InnoDB for reliability where supported.
- Cache expensive query results when appropriate.

Accessibility baseline
- Keyboard support for all interactive controls.
- Visible focus states.
- `aria-label`/`aria-describedby` for custom controls.
- `aria-live` for dynamic status updates.
- Associate errors with fields (`aria-describedby` + `aria-invalid`).
- Required inputs use `aria-required`.
- Modal accessibility requires focus trap and focus restore.
- ALWAYS manage focus in modals.
- Responsive behavior must remain usable at 200% zoom.
- Do not rely on color alone for status communication.

Mistakes to avoid
| Mistake | Fix |
|---|---|
| Missing ABSPATH guard | Add guard to every PHP file |
| Raw superglobal usage | `wp_unslash` + sanitize by type |
| Missing nonce checks | Verify nonce on all writes |
| Missing REST permission callback | Add strict `permission_callback` |
| Unescaped output | Escape at render with context function |
| Wrong text domain | Use `translate-press-zone` |
| Unprefixed options/functions | Use `presszone_translate_` |
| Hardcoded IDs/users in auth logic | Resolve current user dynamically |
| Missing translation wrappers | Localize user-facing strings |
| Hardcoded strings in handlers | Use translation helpers |
| Missing form labels/ARIA links | Add `for`, `aria-describedby`, `aria-required` |
| Hardcoded URLs | Use WP URL helpers (`admin_url`, `home_url`) |
| Missing `wp_unslash()` | Unslash before sanitizing request input |
| Insecure user-defined regex patterns | Escape tokens with `preg_quote()` before wildcard expansion |
| `wp_remote_*` for outbound HTTP | Use `wp_safe_remote_*` (prevents SSRF) |
| Unconditional `error_log()` | Wrap in `if (defined('WP_DEBUG') && WP_DEBUG)` |
| Missing `return` after `wp_send_json_*` | Always return/exit after JSON responses |
| `'style' => true` in `wp_kses` | Remove — allows CSS-based XSS |
| `is_user_logged_in` on admin endpoints | Use `current_user_can('manage_options')` |
| Incomplete `uninstall.php` | Audit all `update_option`/`add_option` calls and include every key |
