|
| 1 | +--- |
| 2 | +applyTo: 'wp-content/plugins/**,wp-content/themes/**,**/*.php,**/*.inc,**/*.js,**/*.jsx,**/*.ts,**/*.tsx,**/*.css,**/*.scss,**/*.json' |
| 3 | +description: 'Coding, security, and testing rules for WordPress plugins and themes' |
| 4 | +--- |
| 5 | + |
| 6 | +# WordPress Development — Copilot Instructions |
| 7 | + |
| 8 | +**Goal:** Generate WordPress code that is secure, performant, testable, and compliant with official WordPress practices. Prefer hooks, small functions, dependency injection (where sensible), and clear separation of concerns. |
| 9 | + |
| 10 | +## 1) Core Principles |
| 11 | +- Never modify WordPress core. Extend via **actions** and **filters**. |
| 12 | +- For plugins, always include a header and guard direct execution in entry PHP files. |
| 13 | +- Use unique prefixes or PHP namespaces to avoid global collisions. |
| 14 | +- Enqueue assets; never inline raw `<script>`/`<style>` in PHP templates. |
| 15 | +- Make user‑facing strings translatable and load the correct text domain. |
| 16 | + |
| 17 | +### Minimal plugin header & guard |
| 18 | +```php |
| 19 | +<?php |
| 20 | +defined('ABSPATH') || exit; |
| 21 | +/** |
| 22 | + * Plugin Name: Awesome Feature |
| 23 | + * Description: Example plugin scaffold. |
| 24 | + * Version: 0.1.0 |
| 25 | + * Author: Example |
| 26 | + * License: GPL-2.0-or-later |
| 27 | + * Text Domain: awesome-feature |
| 28 | + * Domain Path: /languages |
| 29 | + */ |
| 30 | +``` |
| 31 | + |
| 32 | +## 2) Coding Standards (PHP, JS, CSS, HTML) |
| 33 | +- Follow **WordPress Coding Standards (WPCS)** and write DocBlocks for public APIs. |
| 34 | +- PHP: Prefer strict comparisons (`===`, `!==`) where appropriate. Be consistent with array syntax and spacing as per WPCS. |
| 35 | +- JS: Match WordPress JS style; prefer `@wordpress/*` packages for block/editor code. |
| 36 | +- CSS: Use BEM‑like class naming when helpful; avoid over‑specific selectors. |
| 37 | +- PHP 7.4+ compatible patterns unless the project specifies higher. Avoid using features not supported by target WP/PHP versions. |
| 38 | + |
| 39 | +### Linting setup suggestions |
| 40 | +```xml |
| 41 | +<!-- phpcs.xml --> |
| 42 | +<?xml version="1.0"?> |
| 43 | +<ruleset name="Project WPCS"> |
| 44 | + <description>WordPress Coding Standards for this project.</description> |
| 45 | + <file>./</file> |
| 46 | + <exclude-pattern>vendor/*</exclude-pattern> |
| 47 | + <exclude-pattern>node_modules/*</exclude-pattern> |
| 48 | + <rule ref="WordPress"/> |
| 49 | + <rule ref="WordPress-Docs"/> |
| 50 | + <rule ref="WordPress-Extra"/> |
| 51 | + <rule ref="PHPCompatibility"/> |
| 52 | + <config name="testVersion" value="7.4-"/> |
| 53 | +</ruleset> |
| 54 | +``` |
| 55 | + |
| 56 | +```json |
| 57 | +// composer.json (snippet) |
| 58 | +{ |
| 59 | + "require-dev": { |
| 60 | + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", |
| 61 | + "wp-coding-standards/wpcs": "^3.0", |
| 62 | + "phpcompatibility/php-compatibility": "^9.0" |
| 63 | + }, |
| 64 | + "scripts": { |
| 65 | + "lint:php": "phpcs -p", |
| 66 | + "fix:php": "phpcbf -p" |
| 67 | + } |
| 68 | +} |
| 69 | +``` |
| 70 | + |
| 71 | +```json |
| 72 | +// package.json (snippet) |
| 73 | +{ |
| 74 | + "devDependencies": { |
| 75 | + "@wordpress/eslint-plugin": "^x.y.z" |
| 76 | + }, |
| 77 | + "scripts": { |
| 78 | + "lint:js": "eslint ." |
| 79 | + } |
| 80 | +} |
| 81 | +``` |
| 82 | + |
| 83 | +## 3) Security & Data Handling |
| 84 | +- **Escape on output, sanitize on input.** |
| 85 | + - Escape: `esc_html()`, `esc_attr()`, `esc_url()`, `wp_kses_post()`. |
| 86 | + - Sanitize: `sanitize_text_field()`, `sanitize_email()`, `sanitize_key()`, `absint()`, `intval()`. |
| 87 | +- **Capabilities & nonces** for forms, AJAX, REST: |
| 88 | + - Add nonces with `wp_nonce_field()` and verify via `check_admin_referer()` / `wp_verify_nonce()`. |
| 89 | + - Restrict mutations with `current_user_can( 'manage_options' /* or specific cap */ )`. |
| 90 | +- **Database:** always use `$wpdb->prepare()` with placeholders; never concatenate untrusted input. |
| 91 | +- **Uploads:** validate MIME/type and use `wp_handle_upload()`/`media_handle_upload()`. |
| 92 | + |
| 93 | +## 4) Internationalization (i18n) |
| 94 | +- Wrap user‑visible strings with translation functions using your text domain: |
| 95 | + - `__( 'Text', 'awesome-feature' )`, `_x()`, `esc_html__()`. |
| 96 | +- Load translations with `load_plugin_textdomain()` or `load_theme_textdomain()`. |
| 97 | +- Keep a `.pot` in `/languages` and ensure consistent domain usage. |
| 98 | + |
| 99 | +## 5) Performance |
| 100 | +- Defer heavy logic to specific hooks; avoid expensive work on `init`/`wp_loaded` unless necessary. |
| 101 | +- Use transients or object caching for expensive queries; plan invalidation. |
| 102 | +- Enqueue only what you need and conditionally (front vs admin; specific screens/routes). |
| 103 | +- Prefer paginated/parameterized queries over unbounded loops. |
| 104 | + |
| 105 | +## 6) Admin UI & Settings |
| 106 | +- Use **Settings API** for options pages; provide `sanitize_callback` for each setting. |
| 107 | +- For tables, follow `WP_List_Table` patterns. For notices, use the admin notices API. |
| 108 | +- Avoid direct HTML echoing for complex UIs; prefer templates or small view helpers with escaping. |
| 109 | + |
| 110 | +## 7) REST API |
| 111 | +- Register with `register_rest_route()`; always set a `permission_callback`. |
| 112 | +- Validate/sanitize request args via the `args` schema. |
| 113 | +- Return `WP_REST_Response` or arrays/objects that map cleanly to JSON. |
| 114 | + |
| 115 | +## 8) Blocks & Editor (Gutenberg) |
| 116 | +- Use `block.json` + `register_block_type()`; rely on `@wordpress/*` packages. |
| 117 | +- Provide server render callbacks when needed (dynamic blocks). |
| 118 | +- E2E tests should cover: insert block → edit → save → front‑end render. |
| 119 | + |
| 120 | +## 9) Asset Loading |
| 121 | +```php |
| 122 | +add_action('wp_enqueue_scripts', function () { |
| 123 | + wp_enqueue_style( |
| 124 | + 'af-frontend', |
| 125 | + plugins_url('assets/frontend.css', __FILE__), |
| 126 | + [], |
| 127 | + '0.1.0' |
| 128 | + ); |
| 129 | + |
| 130 | + wp_enqueue_script( |
| 131 | + 'af-frontend', |
| 132 | + plugins_url('assets/frontend.js', __FILE__), |
| 133 | + [ 'wp-i18n', 'wp-element' ], |
| 134 | + '0.1.0', |
| 135 | + true |
| 136 | + ); |
| 137 | +}); |
| 138 | +``` |
| 139 | +- Use `wp_register_style/script` to register first if multiple components depend on the same assets. |
| 140 | +- For admin screens, hook into `admin_enqueue_scripts` and check screen IDs. |
| 141 | + |
| 142 | +## 10) Testing |
| 143 | +### PHP Unit/Integration |
| 144 | +- Use **WordPress test suite** with `PHPUnit` and `WP_UnitTestCase`. |
| 145 | +- Test: sanitization, capability checks, REST permissions, DB queries, hooks. |
| 146 | +- Prefer factories (`self::factory()->post->create()` etc.) to set up fixtures. |
| 147 | + |
| 148 | +```xml |
| 149 | +<!-- phpunit.xml.dist (minimal) --> |
| 150 | +<?xml version="1.0" encoding="UTF-8"?> |
| 151 | +<phpunit bootstrap="tests/bootstrap.php" colors="true"> |
| 152 | + <testsuites> |
| 153 | + <testsuite name="Plugin Test Suite"> |
| 154 | + <directory suffix="Test.php">tests/</directory> |
| 155 | + </testsuite> |
| 156 | + </testsuites> |
| 157 | +</phpunit> |
| 158 | +``` |
| 159 | + |
| 160 | +```php |
| 161 | +// tests/bootstrap.php (minimal sketch) |
| 162 | +<?php |
| 163 | +$_tests_dir = getenv('WP_TESTS_DIR') ?: '/tmp/wordpress-tests-lib'; |
| 164 | +require_once $_tests_dir . '/includes/functions.php'; |
| 165 | +tests_add_filter( 'muplugins_loaded', function () { |
| 166 | + require dirname(__DIR__) . '/awesome-feature.php'; |
| 167 | +} ); |
| 168 | +require $_tests_dir . '/includes/bootstrap.php'; |
| 169 | +``` |
| 170 | +### E2E |
| 171 | +- Use Playwright (or Puppeteer) for editor/front‑end flows. |
| 172 | +- Cover basic user journeys and regressions (block insertion, settings save, front‑end render). |
| 173 | + |
| 174 | +## 11) Documentation & Commits |
| 175 | +- Keep `README.md` up to date: install, usage, capabilities, hooks/filters, and test instructions. |
| 176 | +- Use clear, imperative commit messages; reference issues/tickets and summarize impact. |
| 177 | + |
| 178 | +## 12) What Copilot Must Ensure (Checklist) |
| 179 | +- ✅ Unique prefixes/namespaces; no accidental globals. |
| 180 | +- ✅ Nonce + capability checks for any write action (AJAX/REST/forms). |
| 181 | +- ✅ Inputs sanitized; outputs escaped. |
| 182 | +- ✅ User‑visible strings wrapped in i18n with correct text domain. |
| 183 | +- ✅ Assets enqueued via APIs (no inline script/style). |
| 184 | +- ✅ Tests added/updated for new behaviors. |
| 185 | +- ✅ Code passes PHPCS (WPCS) and ESLint where applicable. |
| 186 | +- ✅ Avoid direct DB concatenation; always prepare queries. |
0 commit comments