Wordpress Developer Standard Operating Procedure (SOP)

1. Set Up Local Development Environment

  • Install a local WordPress stack: Use a local web server (Apache or Nginx) with PHP and MySQL (LAMP/MAMP/XAMPP/Docker). A WordPress dev site should have all three components (webserver, PHP, database)deliciousbrains.com. Tools like LocalWP, Laravel Valet, or Docker containers can speed this up.

  • Use version control: Initialize a Git repository for your plugin folder from the start. Commit the scaffold and code changes as you go, even if working alone.

  • Use WP-CLI for scaffolding: Install WP-CLI and run wp scaffold plugin your-plugin-name to generate a boilerplate (main plugin file and readme)developer.wordpress.org. This ensures correct headers (Plugin Name, Version, etc. in the plugin’s main PHP file) and includes a readme.txt. (Ensure your main PHP file has the required header fieldsdeveloper.wordpress.org.)

  • Set development mode: In wp-config.php, enable debugging with define('WP_DEBUG', true), and consider setting WP_ENVIRONMENT_TYPE to “development”deliciousbrains.com so you can conditionally load dev-only code.

  • Check connectivity tools: You may need curl, git, composer, etc. If using Docker or a VM, configure Xdebug or similar for step-by-step debugging.

2. Initialize Plugin Structure and Organization

  • Root files: At the plugin’s root (plugin folder), place the main plugin file (e.g. my-plugin.php) and optionally uninstall.phpdeveloper.wordpress.org. Follow the WP Coding Standards for headers (Plugin Name, Description, Version, Author, License)developer.wordpress.org. Include a license (GPLv2 or later) in comments.

  • Folder hierarchy: Create subdirectories for logical grouping. For example: /admin (backend code), /public (frontend code), /includes (shared PHP libraries), /assets/css, /assets/js, /languages for translations, etcdeveloper.wordpress.org. A clear structure helps maintainability.

  • Prefix or namespace code: To avoid collisions, prefix all functions, classes, global variables, options and transients with a unique plugin-specific prefix (at least 4–5 characters)developer.wordpress.org. For example, use ecpt_ or a namespaced class instead of generic names. Avoid core prefixes like wp_ or double underscoresdeveloper.wordpress.org.

  • Prevent direct access: In every PHP file that outputs code (especially the main file), start with:

    if (!defined('ABSPATH')) {
        exit; // Exit if accessed directly
    }
    

    This stops outside HTTP calls to plugin PHP filesdeveloper.wordpress.org.

  • Separate admin vs. public: Use is_admin() (or current_user_can()) to load admin-only files. For example, wrap admin includes in if (is_admin()) { require __DIR__.'/admin/admin-page.php'; }developer.wordpress.org. Always follow with capability checks, since is_admin() only checks context, not privilegesdeveloper.wordpress.orgdeveloper.wordpress.org.

  • Organize code: For small plugins, procedural code may suffice, but for complex logic use classes or a plugin bootstrap. Keep functions and methods focused (single responsibility). Use autoloaders (PSR-4) or require files manually. WP-CLI’s scaffold already sets up a basic PSR-4 structure and PHPUnit configdeveloper.wordpress.org.

3. Integrate AI APIs (OpenAI ChatGPT, ElevenLabs TTS, MCP)

  • Securely store API keys: Do not hardcode secrets in source. Store API keys in wp-config.php, in a protected option (with add_option and capability checks), or use environment variables. This follows best practice: “never hardcode API keys… store them in wp-config.php, WP options, or env variables”cozythemes.com. (You can also encrypt them or restrict usage server-side.)

  • Use the WP HTTP API: Make calls to external AI services with wp_remote_post()/wp_remote_get(). For example, send the user input to the OpenAI endpoint (using their REST API) and handle the JSON response. Always check for errors: use is_wp_error($response) after wp_remote_…, then parse with wp_remote_retrieve_body()developer.wordpress.orgcozythemes.com.

  • Handle rate limits and retries: Most AI APIs enforce rate limits. Check HTTP headers or status codes (e.g. 429) and, on a limit response, implement retries with exponential backoffcozythemes.com. For example, if you get a 429, schedule a retry later (using wp_schedule_single_event or a background job) or store the request in a transient and re-try. Log such events for troubleshootingcozythemes.com.

  • Use caching for frequent calls: If your plugin frequently fetches the same data (for example, repeated prompts, or identical TTS conversions), cache the results. Use the Transients API or WP Object Cache to store API responses temporarilycozythemes.com. This speeds up performance and avoids unnecessary API quota usage. For instance, cache ChatGPT answers for repeated prompts or store generated audio URLs.

  • Asynchronous processing: For long-running tasks (e.g. large context chains to GPT or generating long audio), don’t do these on page load. Instead, use WP Cron jobs (wp_schedule_event) or a background queue library (like Action Scheduler or wp_remote_post with 'blocking' => false) to process requests in the background. This keeps the UI responsive.

  • ElevenLabs integration: Similar to OpenAI, use wp_remote_post() to call ElevenLabs’ TTS API with text, and retrieve the audio (e.g. MP3) URL. Store or stream the audio as needed. Ensure you have appropriate Content-Type headers and authentication (API key in header or URL).

  • Model Context Protocol (MCP): The Model Context Protocol is an emerging standard for passing context to AI. WordPress has an MCP adapter plugin that “implements MCP to expose WordPress functionality”make.wordpress.org. If relevant, use or reference this. At minimum, design your plugin so its key data/actions can be exposed (e.g. via REST endpoints or via MCP) for other AI tools if needed.

4. Implement Security Best Practices

  • Check user capabilities: For any action or data modification, verify the current user has the appropriate capability using current_user_can(). For example, only users with manage_options or a custom capability should save plugin settings. “Make sure to run your code only when the current user has the necessary capabilities”developer.wordpress.orgdeveloper.wordpress.org.

  • Use nonces for forms/AJAX: Protect all form submissions or AJAX calls with a nonce. Output a nonce field (wp_nonce_field()) in your HTML and verify it on submit with check_admin_referer() (for admin) or check_ajax_referer() (for AJAX)developer.wordpress.org. Nonces prevent CSRF attacks by ensuring the request came from your own site.

  • Validate and sanitize input: Treat all input as untrusted. Use appropriate sanitization before using input. For example, use sanitize_text_field() for plain text, intval() for integers, sanitize_email(), etc. As the handbook says, “sanitizing input is the process of securing/cleaning input data… even admins can enter bad data”developer.wordpress.org. Always sanitize before storing or processing user inputs.

  • Escape all output: When echoing data back to the browser or into HTML attributes, use escaping functions: esc_html(), esc_attr(), esc_url(), etc. You “always want to escape when you echo, not before”developer.wordpress.org. This prevents XSS vulnerabilities. For instance, wrap dynamic content in esc_html() if inside HTML, or esc_attr() if inside an attribute.

  • Use prepared statements for DB queries: If you run any custom SQL with $wpdb, always use $wpdb->prepare() to bind variables safely. This prevents SQL injection. WordPress also provides data APIs (e.g. wp_insert_post, update_option()) that handle sanitization.

  • Limit plugin’s scope: Only load your plugin’s code where needed. Unload hooks or filters if not necessary. Do not execute administrative logic on the public side unless intended. Similarly, never expose raw API keys or sensitive data to the front end or in error messages.

  • Review security standards: Refer to the WordPress security handbook. For example, WP notes that “nonces help protect against several types of attacks including CSRF”developer.wordpress.org and that you should not rely on nonces for auth by themselves, always pair with capability checksdeveloper.wordpress.org.

5. Performance Optimizations

  • Cache API responses: As noted, cache expensive or frequent API results with Transientscozythemes.com. Set reasonable expiration times. This reduces API calls and speeds up user experience. Also cache any static plugin data or user settings (using get_option caching or object caching).

  • Batch and defer heavy work: If you must process many items (e.g. generating audio for multiple posts), do it in batches via scheduled events or AJAX callbacks rather than all at once. Use set_time_limit(0) in background scripts if needed.

  • Load assets conditionally: Only enqueue your CSS/JS on pages where the plugin is active (use admin_enqueue_scripts or wp_enqueue_scripts with page checks). Minify and combine scripts if possible.

  • Lazy loading or async: If your plugin outputs images or embeds (for example, waveforms or audio players), use lazy loading techniques (native loading="lazy" on images, or load audio on user action). If you perform an API call on page load, consider using JavaScript to call it asynchronously after page render.

  • Optimize JavaScript: Defer or async-load non-critical JS. Localize only needed data. Avoid blocking external calls on the front end.

  • Use WP Cron judiciously: Don’t schedule too many cron events. If using WP Cron for retries or updates, ensure tasks don’t overlap and set sensible intervals.

  • Leverage object caching: If the site has an object cache (Redis/Memcached), use it for expensive operations. WordPress’s Transients API can leverage a persistent cache if available.

  • Monitor performance: Even solo, log or monitor slow requests (the CozyThemes guide suggests tracking response timescozythemes.com). If API calls start failing or slowing down, address the cause (retry logic, backoff, or reduce frequency).

6. Testing (Unit, Integration, UI)

  • Automated unit tests: Use PHPUnit with WordPress testing libraries. The WP-CLI “scaffold plugin” command generates a test suite for youdeveloper.wordpress.org. You can install WP unit test framework (bin/install-wp-tests.sh) and write tests in tests/. Test each function or class method in isolation (mocking external calls if needed).

  • Integration tests: Write integration tests that load WordPress and run your plugin code. With WP-CLI you can scaffold plugin tests and run them locallymake.wordpress.orgmake.wordpress.org. For example, wp scaffold plugin-tests my-plugin then bash bin/install-wp-tests.sh to set up. These tests can simulate real plugin interactions (e.g. saving options, AJAX requests).

  • UI and end-to-end testing: For any admin pages or shortcodes, manually verify the UI flows or use a tool like Codeception’s WPBrowser (or headless Chrome). For AJAX/Axios calls, test that the nonce and permission checks work. Even without a full framework, step through the plugin in the WP admin to ensure buttons, settings forms, and outputs behave correctly.

  • Manual regression testing: Each time you change code, test on a clean WordPress install (or your staging environment) with only your plugin active. Check that no errors appear in the log. Also test on the latest WP/PHP versions you support.

  • Linting and standards: Use PHP CodeSniffer with the WordPress Coding Standards (you can generate a .phpcs.xml with WP-CLI scaffolddeveloper.wordpress.org) to catch PHP syntax issues and enforce consistency. For JavaScript, consider ESLint or a style guide.

  • Debug tools: Enable WP_DEBUG_LOG to file and use it to capture runtime errors. The CozyThemes guide also suggests logging API errors for troubleshootingcozythemes.com.

7. Deployment to WordPress Plugin Directory (Optional)

  • Follow directory guidelines: If you plan to publish on wordpress.org, ensure GPLv2 (or later) for all codedeveloper.wordpress.org. Avoid bundling WordPress’s own libraries (like jQuery or PHPMailer) since WP already provides themdeveloper.wordpress.org. Do not include proprietary or disallowed code.

  • Prepare readme.txt: Use the standard plugin readme format. Include sections like “Installation,” “Changelog,” and a proper header with === Plugin Name ===, “Stable tag,” “Requires PHP,” “Requires at least/ Tested up to,” etcdeveloper.wordpress.orgdeveloper.wordpress.org. For example, set “License: GPLv2 or later” under “=== Plugin Name ===”developer.wordpress.org. The Stable Tag must match a version folder or trunkdeveloper.wordpress.org.

  • Versioning and tagging: Increment your plugin version in the header on every release. Follow semantic versioning for releases. Use Subversion (SVN) to push to the repository: put development in /trunk and create a matching /tags/x.y.z/ for each version. (The plugin page will display the version from your main PHP file.) Frequent trivial commits are discourageddeveloper.wordpress.org; only push stable updates.

  • Final checks: Before submission, disable debug output (WP_DEBUG=false), remove any temporary code, and ensure the code is as clean and secure as possible. Submit a ZIP of the plugin code for review. Once approved, users can install it, and follow the Stable Tag update mechanism for future releases.

SOP, WordpressFrancesca Tabor