<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>WordPress Plugin Development Guide Archives - Developry Plugins</title>
	<atom:link href="https://developryplugins.com/category/wordpress-plugin-development-guide/feed/" rel="self" type="application/rss+xml" />
	<link>https://developryplugins.com/category/wordpress-plugin-development-guide/</link>
	<description></description>
	<lastBuildDate>Mon, 24 Nov 2025 11:18:16 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://developryplugins.com/wp-content/uploads/2025/11/cropped-favicon-32x32.png</url>
	<title>WordPress Plugin Development Guide Archives - Developry Plugins</title>
	<link>https://developryplugins.com/category/wordpress-plugin-development-guide/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>WordPress Plugin Development from Scratch: Complete Beginner&#8217;s Guide</title>
		<link>https://developryplugins.com/wordpress-plugin-development-from-scratch-complete-beginners-guide/</link>
		
		<dc:creator><![CDATA[Krasen Slavov]]></dc:creator>
		<pubDate>Fri, 30 Jan 2026 09:00:00 +0000</pubDate>
				<category><![CDATA[WordPress Plugin Development Guide]]></category>
		<category><![CDATA[beginner guide]]></category>
		<category><![CDATA[php development]]></category>
		<category><![CDATA[plugin development]]></category>
		<category><![CDATA[plugin tutorial]]></category>
		<category><![CDATA[wordpress plugins]]></category>
		<guid isPermaLink="false">https://developryplugins.com/?p=171</guid>

					<description><![CDATA[<p>WordPress plugin development opens endless possibilities for extending WordPress functionality. This beginner-friendly guide teaches you to build your first plugin from scratch, even if you’ve never written PHP code before....</p>
<p>The post <a href="https://developryplugins.com/wordpress-plugin-development-from-scratch-complete-beginners-guide/">WordPress Plugin Development from Scratch: Complete Beginner&#8217;s Guide</a> appeared first on <a href="https://developryplugins.com">Developry Plugins</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><!-- @format --></p>
<p>WordPress plugin development opens endless possibilities for extending WordPress functionality. This beginner-friendly guide teaches you to build your first plugin from scratch, even if you’ve never written PHP code before. You’ll learn fundamental concepts, best practices, and create a functioning plugin by the end.</p>
<h2 id="what-are-wordpress-plugins">What Are WordPress Plugins?</h2>
<p>Plugins are PHP files that extend WordPress core functionality without modifying core files. They add features, modify behavior, and integrate services. WordPress’s extensive plugin API provides hooks and functions that let you tap into WordPress at precise moments during execution.</p>
<p>Understanding this principle is crucial: never modify WordPress core. Always build plugins (or themes) to add functionality. This keeps your changes upgrade-safe and maintainable.</p>
<h2 id="prerequisites-and-setup">Prerequisites and Setup</h2>
<p>Before starting, ensure you have:</p>
<ul>
<li><strong>Basic PHP knowledge</strong> &#8211; Understand variables, functions, arrays</li>
<li><strong>HTML/CSS basics</strong> &#8211; Know how to structure and style content</li>
<li><strong>Local development environment</strong> &#8211; LocalWP, MAMP, or Docker</li>
<li><strong>Code editor</strong> &#8211; VS Code, PhpStorm, or Sublime Text</li>
</ul>
<p>Install LocalWP for the easiest local WordPress setup. It handles server configuration, making development straightforward for beginners.</p>
<h2 id="your-first-plugin-file">Your First Plugin File</h2>
<p>Create a new folder in <code>wp-content/plugins/</code> named <code>my-first-plugin</code>. Inside, create <code>my-first-plugin.php</code>:</p>
<div class="sourceCode" id="cb1">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true"></a><span class="kw">&lt;?php</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true"></a><span class="co">/**</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true"></a><span class="co"> * Plugin Name: My First Plugin</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true"></a><span class="co"> * Plugin URI: https://example.com/my-first-plugin</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true"></a><span class="co"> * Description: A simple plugin to learn WordPress development</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true"></a><span class="co"> * Version: 1.0.0</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true"></a><span class="co"> * Author: Your Name</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true"></a><span class="co"> * Author URI: https://example.com</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true"></a><span class="co"> * License: GPL v2 or later</span></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true"></a><span class="co"> * License URI: https://www.gnu.org/licenses/gpl-2.0.html</span></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true"></a><span class="co"> * Text Domain: my-first-plugin</span></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true"></a><span class="co"> * Domain Path: /languages</span></span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true"></a><span class="co"> */</span></span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true"></a></span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true"></a><span class="co">// If this file is called directly, abort.</span></span>
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true"></a><span class="kw">if</span> <span class="ot">(</span> ! <span class="fu">defined</span><span class="ot">(</span> <span class="st">&#39;WPINC&#39;</span> <span class="ot">)</span> <span class="ot">)</span> {</span>
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true"></a>    <span class="kw">die</span><span class="ot">;</span></span>
<span id="cb1-18"><a href="#cb1-18" aria-hidden="true"></a>}</span></code></pre>
</div>
<p>The header comment tells WordPress about your plugin. Required fields are Plugin Name and Description. The security check prevents direct file access.</p>
<p>Navigate to Plugins in your WordPress admin. You’ll see “My First Plugin” listed. Click Activate.</p>
<h2 id="understanding-wordpress-hooks">Understanding WordPress Hooks</h2>
<p>Hooks are WordPress’s event system. They allow your plugin to execute code at specific moments.</p>
<p><strong>Actions</strong> let you run code at specific points:</p>
<div class="sourceCode" id="cb2">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true"></a><span class="kw">function</span> dprt_welcome_message<span class="ot">()</span> {</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true"></a>    <span class="kw">echo</span> <span class="st">&#39;&lt;div class=&quot;notice notice-info&quot;&gt;&lt;p&gt;Welcome to our site!&lt;/p&gt;&lt;/div&gt;&#39;</span><span class="ot">;</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true"></a>}</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true"></a>add_action<span class="ot">(</span> <span class="st">&#39;admin_notices&#39;</span><span class="ot">,</span> <span class="st">&#39;dprt_welcome_message&#39;</span> <span class="ot">);</span></span></code></pre>
</div>
<p>This displays a message in the WordPress admin when the <code>admin_notices</code> action fires.</p>
<p><strong>Filters</strong> let you modify data:</p>
<div class="sourceCode" id="cb3">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true"></a><span class="kw">function</span> dprt_modify_excerpt_length<span class="ot">(</span> <span class="kw">$length</span> <span class="ot">)</span> {</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true"></a>    <span class="kw">return</span> <span class="dv">30</span><span class="ot">;</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true"></a>}</span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true"></a>add_filter<span class="ot">(</span> <span class="st">&#39;excerpt_length&#39;</span><span class="ot">,</span> <span class="st">&#39;dprt_modify_excerpt_length&#39;</span> <span class="ot">);</span></span></code></pre>
</div>
<p>This changes excerpt length to 30 words by filtering the default value.</p>
<h2 id="common-hooks-for-beginners">Common Hooks for Beginners</h2>
<p>Start with these essential hooks:</p>
<p><strong><code>init</code></strong> &#8211; Fires after WordPress finishes loading. Use it for registering post types, taxonomies, and initialization:</p>
<div class="sourceCode" id="cb4">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true"></a><span class="kw">function</span> dprt_init_plugin<span class="ot">()</span> {</span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true"></a>    <span class="co">// Register custom post types, taxonomies, etc.</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true"></a>}</span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true"></a>add_action<span class="ot">(</span> <span class="st">&#39;init&#39;</span><span class="ot">,</span> <span class="st">&#39;dprt_init_plugin&#39;</span> <span class="ot">);</span></span></code></pre>
</div>
<p><strong><code>wp_enqueue_scripts</code></strong> &#8211; Load CSS and JavaScript for frontend:</p>
<div class="sourceCode" id="cb5">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true"></a><span class="kw">function</span> dprt_enqueue_assets<span class="ot">()</span> {</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true"></a>    wp_enqueue_style<span class="ot">(</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true"></a>        <span class="st">&#39;dprt-styles&#39;</span><span class="ot">,</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true"></a>        plugin_dir_url<span class="ot">(</span> <span class="kw">__FILE__</span> <span class="ot">)</span> . <span class="st">&#39;assets/css/style.css&#39;</span><span class="ot">,</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true"></a>        <span class="kw">array</span><span class="ot">(),</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true"></a>        <span class="st">&#39;1.0.0&#39;</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true"></a>    <span class="ot">);</span></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true"></a></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true"></a>    wp_enqueue_script<span class="ot">(</span></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true"></a>        <span class="st">&#39;dprt-scripts&#39;</span><span class="ot">,</span></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true"></a>        plugin_dir_url<span class="ot">(</span> <span class="kw">__FILE__</span> <span class="ot">)</span> . <span class="st">&#39;assets/js/script.js&#39;</span><span class="ot">,</span></span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true"></a>        <span class="kw">array</span><span class="ot">(</span> <span class="st">&#39;jquery&#39;</span> <span class="ot">),</span></span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true"></a>        <span class="st">&#39;1.0.0&#39;</span><span class="ot">,</span></span>
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true"></a>        <span class="kw">true</span></span>
<span id="cb5-15"><a href="#cb5-15" aria-hidden="true"></a>    <span class="ot">);</span></span>
<span id="cb5-16"><a href="#cb5-16" aria-hidden="true"></a>}</span>
<span id="cb5-17"><a href="#cb5-17" aria-hidden="true"></a>add_action<span class="ot">(</span> <span class="st">&#39;wp_enqueue_scripts&#39;</span><span class="ot">,</span> <span class="st">&#39;dprt_enqueue_assets&#39;</span> <span class="ot">);</span></span></code></pre>
</div>
<p><strong><code>admin_menu</code></strong> &#8211; Add admin menu pages:</p>
<div class="sourceCode" id="cb6">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true"></a><span class="kw">function</span> dprt_add_menu_page<span class="ot">()</span> {</span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true"></a>    add_menu_page<span class="ot">(</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true"></a>        <span class="st">&#39;My Plugin Settings&#39;</span><span class="ot">,</span>  <span class="co">// Page title</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true"></a>        <span class="st">&#39;My Plugin&#39;</span><span class="ot">,</span>           <span class="co">// Menu title</span></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true"></a>        <span class="st">&#39;manage_options&#39;</span><span class="ot">,</span>      <span class="co">// Capability required</span></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true"></a>        <span class="st">&#39;my-plugin&#39;</span><span class="ot">,</span>           <span class="co">// Menu slug</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true"></a>        <span class="st">&#39;dprt_settings_page&#39;</span><span class="ot">,</span>  <span class="co">// Function to display page</span></span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true"></a>        <span class="st">&#39;dashicons-admin-generic&#39;</span><span class="ot">,</span> <span class="co">// Icon</span></span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true"></a>        <span class="dv">20</span>                     <span class="co">// Position</span></span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true"></a>    <span class="ot">);</span></span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true"></a>}</span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true"></a>add_action<span class="ot">(</span> <span class="st">&#39;admin_menu&#39;</span><span class="ot">,</span> <span class="st">&#39;dprt_add_menu_page&#39;</span> <span class="ot">);</span></span>
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true"></a></span>
<span id="cb6-14"><a href="#cb6-14" aria-hidden="true"></a><span class="kw">function</span> dprt_settings_page<span class="ot">()</span> {</span>
<span id="cb6-15"><a href="#cb6-15" aria-hidden="true"></a>    <span class="kw">echo</span> <span class="st">&#39;&lt;div class=&quot;wrap&quot;&gt;&#39;</span><span class="ot">;</span></span>
<span id="cb6-16"><a href="#cb6-16" aria-hidden="true"></a>    <span class="kw">echo</span> <span class="st">&#39;&lt;h1&gt;My Plugin Settings&lt;/h1&gt;&#39;</span><span class="ot">;</span></span>
<span id="cb6-17"><a href="#cb6-17" aria-hidden="true"></a>    <span class="kw">echo</span> <span class="st">&#39;&lt;p&gt;Settings page content goes here.&lt;/p&gt;&#39;</span><span class="ot">;</span></span>
<span id="cb6-18"><a href="#cb6-18" aria-hidden="true"></a>    <span class="kw">echo</span> <span class="st">&#39;&lt;/div&gt;&#39;</span><span class="ot">;</span></span>
<span id="cb6-19"><a href="#cb6-19" aria-hidden="true"></a>}</span></code></pre>
</div>
<h2 id="plugin-activation-and-deactivation">Plugin Activation and Deactivation</h2>
<p>Run code when plugins activate or deactivate:</p>
<div class="sourceCode" id="cb7">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true"></a><span class="kw">function</span> dprt_activate_plugin<span class="ot">()</span> {</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true"></a>    <span class="co">// Set default options</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true"></a>    add_option<span class="ot">(</span> <span class="st">&#39;dprt_welcome_message&#39;</span><span class="ot">,</span> <span class="st">&#39;Welcome to our site!&#39;</span> <span class="ot">);</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true"></a></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true"></a>    <span class="co">// Create custom database tables if needed</span></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true"></a>    <span class="co">// Flush rewrite rules if registering custom post types</span></span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true"></a>    flush_rewrite_rules<span class="ot">();</span></span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true"></a>}</span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true"></a>register_activation_hook<span class="ot">(</span> <span class="kw">__FILE__</span><span class="ot">,</span> <span class="st">&#39;dprt_activate_plugin&#39;</span> <span class="ot">);</span></span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true"></a></span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true"></a><span class="kw">function</span> dprt_deactivate_plugin<span class="ot">()</span> {</span>
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true"></a>    <span class="co">// Clean up temporary data</span></span>
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true"></a>    <span class="co">// Flush rewrite rules</span></span>
<span id="cb7-14"><a href="#cb7-14" aria-hidden="true"></a>    flush_rewrite_rules<span class="ot">();</span></span>
<span id="cb7-15"><a href="#cb7-15" aria-hidden="true"></a></span>
<span id="cb7-16"><a href="#cb7-16" aria-hidden="true"></a>    <span class="co">// Don&#39;t delete user data on deactivation</span></span>
<span id="cb7-17"><a href="#cb7-17" aria-hidden="true"></a>    <span class="co">// Save that for uninstall</span></span>
<span id="cb7-18"><a href="#cb7-18" aria-hidden="true"></a>}</span>
<span id="cb7-19"><a href="#cb7-19" aria-hidden="true"></a>register_deactivation_hook<span class="ot">(</span> <span class="kw">__FILE__</span><span class="ot">,</span> <span class="st">&#39;dprt_deactivate_plugin&#39;</span> <span class="ot">);</span></span></code></pre>
</div>
<p>Never delete user data on deactivation. Users might temporarily deactivate to troubleshoot. Save deletion for uninstall.</p>
<h2 id="the-options-api">The Options API</h2>
<p>Store plugin settings using the Options API:</p>
<div class="sourceCode" id="cb8">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true"></a><span class="co">// Save option</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true"></a>update_option<span class="ot">(</span> <span class="st">&#39;dprt_setting_name&#39;</span><span class="ot">,</span> <span class="st">&#39;value&#39;</span> <span class="ot">);</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true"></a></span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true"></a><span class="co">// Get option</span></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true"></a><span class="kw">$setting</span> = get_option<span class="ot">(</span> <span class="st">&#39;dprt_setting_name&#39;</span><span class="ot">,</span> <span class="st">&#39;default value&#39;</span> <span class="ot">);</span></span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true"></a></span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true"></a><span class="co">// Delete option</span></span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true"></a>delete_option<span class="ot">(</span> <span class="st">&#39;dprt_setting_name&#39;</span> <span class="ot">);</span></span></code></pre>
</div>
<p>Options are stored in the wp_options table. Use them for plugin configuration, user preferences, and cached data.</p>
<h2 id="plugin-security-basics">Plugin Security Basics</h2>
<p>Security is paramount. Follow these practices from day one:</p>
<p><strong>Sanitize input</strong> &#8211; Clean data users provide:</p>
<div class="sourceCode" id="cb9">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true"></a><span class="kw">$user_input</span> = sanitize_text_field<span class="ot">(</span> <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;input_field&#39;</span><span class="ot">]</span> <span class="ot">);</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true"></a><span class="kw">$email</span> = sanitize_email<span class="ot">(</span> <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;email_field&#39;</span><span class="ot">]</span> <span class="ot">);</span></span></code></pre>
</div>
<p><strong>Escape output</strong> &#8211; Prevent XSS attacks:</p>
<div class="sourceCode" id="cb10">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true"></a><span class="kw">echo</span> <span class="st">&#39;&lt;p&gt;&#39;</span> . esc_html<span class="ot">(</span> <span class="kw">$user_data</span> <span class="ot">)</span> . <span class="st">&#39;&lt;/p&gt;&#39;</span><span class="ot">;</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true"></a><span class="kw">echo</span> <span class="st">&#39;&lt;a href=&quot;&#39;</span> . esc_url<span class="ot">(</span> <span class="kw">$link</span> <span class="ot">)</span> . <span class="st">&#39;&quot;&gt;Link&lt;/a&gt;&#39;</span><span class="ot">;</span></span></code></pre>
</div>
<p><strong>Use nonces</strong> &#8211; Verify form submissions:</p>
<div class="sourceCode" id="cb11">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true"></a><span class="co">// Create nonce</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true"></a>wp_nonce_field<span class="ot">(</span> <span class="st">&#39;dprt_save_settings&#39;</span><span class="ot">,</span> <span class="st">&#39;dprt_settings_nonce&#39;</span> <span class="ot">);</span></span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true"></a></span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true"></a><span class="co">// Verify nonce</span></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true"></a><span class="kw">if</span> <span class="ot">(</span> ! wp_verify_nonce<span class="ot">(</span> <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;dprt_settings_nonce&#39;</span><span class="ot">],</span> <span class="st">&#39;dprt_save_settings&#39;</span> <span class="ot">)</span> <span class="ot">)</span> {</span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true"></a>    wp_die<span class="ot">(</span> <span class="st">&#39;Security check failed&#39;</span> <span class="ot">);</span></span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true"></a>}</span></code></pre>
</div>
<p><strong>Check capabilities</strong> &#8211; Ensure users have permission:</p>
<div class="sourceCode" id="cb12">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true"></a><span class="kw">if</span> <span class="ot">(</span> ! current_user_can<span class="ot">(</span> <span class="st">&#39;manage_options&#39;</span> <span class="ot">)</span> <span class="ot">)</span> {</span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true"></a>    wp_die<span class="ot">(</span> <span class="st">&#39;Unauthorized access&#39;</span> <span class="ot">);</span></span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true"></a>}</span></code></pre>
</div>
<h2 id="organizing-plugin-files">Organizing Plugin Files</h2>
<p>As plugins grow, organize code across multiple files:</p>
<pre><code>my-first-plugin/
├── my-first-plugin.php    (Main file)
├── includes/
│   ├── class-admin.php
│   ├── class-frontend.php
│   └── functions.php
├── assets/
│   ├── css/
│   │   └── style.css
│   └── js/
│       └── script.js
└── languages/</code></pre>
<p>Include files in your main plugin file:</p>
<div class="sourceCode" id="cb14">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true"></a><span class="kw">require_once</span> plugin_dir_path<span class="ot">(</span> <span class="kw">__FILE__</span> <span class="ot">)</span> . <span class="st">&#39;includes/functions.php&#39;</span><span class="ot">;</span></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true"></a><span class="kw">require_once</span> plugin_dir_path<span class="ot">(</span> <span class="kw">__FILE__</span> <span class="ot">)</span> . <span class="st">&#39;includes/class-admin.php&#39;</span><span class="ot">;</span></span></code></pre>
</div>
<h2 id="debugging-your-plugin">Debugging Your Plugin</h2>
<p>Enable WordPress debugging in wp-config.php:</p>
<div class="sourceCode" id="cb15">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true"></a><span class="fu">define</span><span class="ot">(</span> <span class="st">&#39;WP_DEBUG&#39;</span><span class="ot">,</span> <span class="kw">true</span> <span class="ot">);</span></span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true"></a><span class="fu">define</span><span class="ot">(</span> <span class="st">&#39;WP_DEBUG_LOG&#39;</span><span class="ot">,</span> <span class="kw">true</span> <span class="ot">);</span></span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true"></a><span class="fu">define</span><span class="ot">(</span> <span class="st">&#39;WP_DEBUG_DISPLAY&#39;</span><span class="ot">,</span> <span class="kw">false</span> <span class="ot">);</span></span></code></pre>
</div>
<p>Log debug messages:</p>
<div class="sourceCode" id="cb16">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true"></a><span class="fu">error_log</span><span class="ot">(</span> <span class="st">&#39;Debug message: &#39;</span> . <span class="fu">print_r</span><span class="ot">(</span> <span class="kw">$variable</span><span class="ot">,</span> <span class="kw">true</span> <span class="ot">)</span> <span class="ot">);</span></span></code></pre>
</div>
<p>Check <code>/wp-content/debug.log</code> for logged errors.</p>
<h2 id="building-a-complete-example-plugin">Building a Complete Example Plugin</h2>
<p>Let’s build a simple quote display plugin:</p>
<div class="sourceCode" id="cb17">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true"></a><span class="kw">&lt;?php</span></span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true"></a><span class="co">/**</span></span>
<span id="cb17-3"><a href="#cb17-3" aria-hidden="true"></a><span class="co"> * Plugin Name: Simple Quote Display</span></span>
<span id="cb17-4"><a href="#cb17-4" aria-hidden="true"></a><span class="co"> * Description: Display random quotes on your site</span></span>
<span id="cb17-5"><a href="#cb17-5" aria-hidden="true"></a><span class="co"> * Version: 1.0.0</span></span>
<span id="cb17-6"><a href="#cb17-6" aria-hidden="true"></a><span class="co"> */</span></span>
<span id="cb17-7"><a href="#cb17-7" aria-hidden="true"></a></span>
<span id="cb17-8"><a href="#cb17-8" aria-hidden="true"></a><span class="kw">if</span> <span class="ot">(</span> ! <span class="fu">defined</span><span class="ot">(</span> <span class="st">&#39;WPINC&#39;</span> <span class="ot">)</span> <span class="ot">)</span> {</span>
<span id="cb17-9"><a href="#cb17-9" aria-hidden="true"></a>    <span class="kw">die</span><span class="ot">;</span></span>
<span id="cb17-10"><a href="#cb17-10" aria-hidden="true"></a>}</span>
<span id="cb17-11"><a href="#cb17-11" aria-hidden="true"></a></span>
<span id="cb17-12"><a href="#cb17-12" aria-hidden="true"></a><span class="co">// Add admin menu</span></span>
<span id="cb17-13"><a href="#cb17-13" aria-hidden="true"></a><span class="kw">function</span> sqd_add_menu<span class="ot">()</span> {</span>
<span id="cb17-14"><a href="#cb17-14" aria-hidden="true"></a>    add_menu_page<span class="ot">(</span></span>
<span id="cb17-15"><a href="#cb17-15" aria-hidden="true"></a>        <span class="st">&#39;Quotes&#39;</span><span class="ot">,</span></span>
<span id="cb17-16"><a href="#cb17-16" aria-hidden="true"></a>        <span class="st">&#39;Quotes&#39;</span><span class="ot">,</span></span>
<span id="cb17-17"><a href="#cb17-17" aria-hidden="true"></a>        <span class="st">&#39;manage_options&#39;</span><span class="ot">,</span></span>
<span id="cb17-18"><a href="#cb17-18" aria-hidden="true"></a>        <span class="st">&#39;sqd-quotes&#39;</span><span class="ot">,</span></span>
<span id="cb17-19"><a href="#cb17-19" aria-hidden="true"></a>        <span class="st">&#39;sqd_quotes_page&#39;</span><span class="ot">,</span></span>
<span id="cb17-20"><a href="#cb17-20" aria-hidden="true"></a>        <span class="st">&#39;dashicons-format-quote&#39;</span></span>
<span id="cb17-21"><a href="#cb17-21" aria-hidden="true"></a>    <span class="ot">);</span></span>
<span id="cb17-22"><a href="#cb17-22" aria-hidden="true"></a>}</span>
<span id="cb17-23"><a href="#cb17-23" aria-hidden="true"></a>add_action<span class="ot">(</span> <span class="st">&#39;admin_menu&#39;</span><span class="ot">,</span> <span class="st">&#39;sqd_add_menu&#39;</span> <span class="ot">);</span></span>
<span id="cb17-24"><a href="#cb17-24" aria-hidden="true"></a></span>
<span id="cb17-25"><a href="#cb17-25" aria-hidden="true"></a><span class="co">// Admin page</span></span>
<span id="cb17-26"><a href="#cb17-26" aria-hidden="true"></a><span class="kw">function</span> sqd_quotes_page<span class="ot">()</span> {</span>
<span id="cb17-27"><a href="#cb17-27" aria-hidden="true"></a>    <span class="kw">if</span> <span class="ot">(</span> <span class="kw">isset</span><span class="ot">(</span> <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;sqd_add_quote&#39;</span><span class="ot">]</span> <span class="ot">)</span> <span class="ot">)</span> {</span>
<span id="cb17-28"><a href="#cb17-28" aria-hidden="true"></a>        <span class="kw">if</span> <span class="ot">(</span> ! wp_verify_nonce<span class="ot">(</span> <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;sqd_nonce&#39;</span><span class="ot">],</span> <span class="st">&#39;sqd_add_quote&#39;</span> <span class="ot">)</span> <span class="ot">)</span> {</span>
<span id="cb17-29"><a href="#cb17-29" aria-hidden="true"></a>            wp_die<span class="ot">(</span> <span class="st">&#39;Security check failed&#39;</span> <span class="ot">);</span></span>
<span id="cb17-30"><a href="#cb17-30" aria-hidden="true"></a>        }</span>
<span id="cb17-31"><a href="#cb17-31" aria-hidden="true"></a></span>
<span id="cb17-32"><a href="#cb17-32" aria-hidden="true"></a>        <span class="kw">$quote</span> = sanitize_textarea_field<span class="ot">(</span> <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;quote&#39;</span><span class="ot">]</span> <span class="ot">);</span></span>
<span id="cb17-33"><a href="#cb17-33" aria-hidden="true"></a>        <span class="kw">$quotes</span> = get_option<span class="ot">(</span> <span class="st">&#39;sqd_quotes&#39;</span><span class="ot">,</span> <span class="kw">array</span><span class="ot">()</span> <span class="ot">);</span></span>
<span id="cb17-34"><a href="#cb17-34" aria-hidden="true"></a>        <span class="kw">$quotes</span><span class="ot">[]</span> = <span class="kw">$quote</span><span class="ot">;</span></span>
<span id="cb17-35"><a href="#cb17-35" aria-hidden="true"></a>        update_option<span class="ot">(</span> <span class="st">&#39;sqd_quotes&#39;</span><span class="ot">,</span> <span class="kw">$quotes</span> <span class="ot">);</span></span>
<span id="cb17-36"><a href="#cb17-36" aria-hidden="true"></a>    }</span>
<span id="cb17-37"><a href="#cb17-37" aria-hidden="true"></a></span>
<span id="cb17-38"><a href="#cb17-38" aria-hidden="true"></a>    <span class="kw">$quotes</span> = get_option<span class="ot">(</span> <span class="st">&#39;sqd_quotes&#39;</span><span class="ot">,</span> <span class="kw">array</span><span class="ot">()</span> <span class="ot">);</span></span>
<span id="cb17-39"><a href="#cb17-39" aria-hidden="true"></a>    <span class="kw">?&gt;</span></span>
<span id="cb17-40"><a href="#cb17-40" aria-hidden="true"></a>    &lt;div <span class="kw">class</span>=<span class="st">&quot;wrap&quot;</span>&gt;</span>
<span id="cb17-41"><a href="#cb17-41" aria-hidden="true"></a>        &lt;h1&gt;Manage Quotes&lt;/h1&gt;</span>
<span id="cb17-42"><a href="#cb17-42" aria-hidden="true"></a>        &lt;form method=<span class="st">&quot;post&quot;</span>&gt;</span>
<span id="cb17-43"><a href="#cb17-43" aria-hidden="true"></a>            &lt;<span class="ot">?</span>php wp_nonce_field<span class="ot">(</span> <span class="st">&#39;sqd_add_quote&#39;</span><span class="ot">,</span> <span class="st">&#39;sqd_nonce&#39;</span> <span class="ot">);</span> <span class="kw">?&gt;</span></span>
<span id="cb17-44"><a href="#cb17-44" aria-hidden="true"></a>            &lt;textarea name=<span class="st">&quot;quote&quot;</span> rows=<span class="st">&quot;3&quot;</span> cols=<span class="st">&quot;50&quot;</span>&gt;&lt;/textarea&gt;</span>
<span id="cb17-45"><a href="#cb17-45" aria-hidden="true"></a>            &lt;input type=<span class="st">&quot;submit&quot;</span> name=<span class="st">&quot;sqd_add_quote&quot;</span> value=<span class="st">&quot;Add Quote&quot;</span> <span class="kw">class</span>=<span class="st">&quot;button button-primary&quot;</span>&gt;</span>
<span id="cb17-46"><a href="#cb17-46" aria-hidden="true"></a>        &lt;/form&gt;</span>
<span id="cb17-47"><a href="#cb17-47" aria-hidden="true"></a>        &lt;h2&gt;Existing Quotes&lt;/h2&gt;</span>
<span id="cb17-48"><a href="#cb17-48" aria-hidden="true"></a>        &lt;ul&gt;</span>
<span id="cb17-49"><a href="#cb17-49" aria-hidden="true"></a>            &lt;<span class="ot">?</span>php <span class="kw">foreach</span> <span class="ot">(</span> <span class="kw">$quotes</span> <span class="kw">as</span> <span class="kw">$quote</span> <span class="ot">)</span> <span class="ot">:</span> <span class="kw">?&gt;</span></span>
<span id="cb17-50"><a href="#cb17-50" aria-hidden="true"></a>                &lt;li&gt;&lt;<span class="ot">?</span>php <span class="kw">echo</span> esc_html<span class="ot">(</span> <span class="kw">$quote</span> <span class="ot">);</span> <span class="kw">?&gt;</span>&lt;/li&gt;</span>
<span id="cb17-51"><a href="#cb17-51" aria-hidden="true"></a>            &lt;<span class="ot">?</span>php <span class="kw">endfor</span>each<span class="ot">;</span> <span class="kw">?&gt;</span></span>
<span id="cb17-52"><a href="#cb17-52" aria-hidden="true"></a>        &lt;/ul&gt;</span>
<span id="cb17-53"><a href="#cb17-53" aria-hidden="true"></a>    &lt;/div&gt;</span>
<span id="cb17-54"><a href="#cb17-54" aria-hidden="true"></a>    &lt;<span class="ot">?</span>php</span>
<span id="cb17-55"><a href="#cb17-55" aria-hidden="true"></a>}</span>
<span id="cb17-56"><a href="#cb17-56" aria-hidden="true"></a></span>
<span id="cb17-57"><a href="#cb17-57" aria-hidden="true"></a><span class="co">// Shortcode to display random quote</span></span>
<span id="cb17-58"><a href="#cb17-58" aria-hidden="true"></a><span class="kw">function</span> sqd_display_quote<span class="ot">()</span> {</span>
<span id="cb17-59"><a href="#cb17-59" aria-hidden="true"></a>    <span class="kw">$quotes</span> = get_option<span class="ot">(</span> <span class="st">&#39;sqd_quotes&#39;</span><span class="ot">,</span> <span class="kw">array</span><span class="ot">()</span> <span class="ot">);</span></span>
<span id="cb17-60"><a href="#cb17-60" aria-hidden="true"></a>    <span class="kw">if</span> <span class="ot">(</span> <span class="kw">empty</span><span class="ot">(</span> <span class="kw">$quotes</span> <span class="ot">)</span> <span class="ot">)</span> {</span>
<span id="cb17-61"><a href="#cb17-61" aria-hidden="true"></a>        <span class="kw">return</span> <span class="st">&#39;&#39;</span><span class="ot">;</span></span>
<span id="cb17-62"><a href="#cb17-62" aria-hidden="true"></a>    }</span>
<span id="cb17-63"><a href="#cb17-63" aria-hidden="true"></a>    <span class="kw">$random_quote</span> = <span class="kw">$quotes</span><span class="ot">[</span> <span class="fu">array_rand</span><span class="ot">(</span> <span class="kw">$quotes</span> <span class="ot">)</span> <span class="ot">];</span></span>
<span id="cb17-64"><a href="#cb17-64" aria-hidden="true"></a>    <span class="kw">return</span> <span class="st">&#39;&lt;blockquote class=&quot;sqd-quote&quot;&gt;&#39;</span> . esc_html<span class="ot">(</span> <span class="kw">$random_quote</span> <span class="ot">)</span> . <span class="st">&#39;&lt;/blockquote&gt;&#39;</span><span class="ot">;</span></span>
<span id="cb17-65"><a href="#cb17-65" aria-hidden="true"></a>}</span>
<span id="cb17-66"><a href="#cb17-66" aria-hidden="true"></a>add_shortcode<span class="ot">(</span> <span class="st">&#39;random_quote&#39;</span><span class="ot">,</span> <span class="st">&#39;sqd_display_quote&#39;</span> <span class="ot">);</span></span></code></pre>
</div>
<p>This complete plugin adds quotes via admin, stores them, and displays them with <code>[random_quote]</code> shortcode.</p>
<h2 id="common-beginner-mistakes">Common Beginner Mistakes</h2>
<p>Avoid these pitfalls:</p>
<ul>
<li><strong>Not using prefixes</strong> &#8211; Prefix all functions to avoid conflicts</li>
<li><strong>Forgetting security</strong> &#8211; Always sanitize, escape, and verify</li>
<li><strong>Ignoring WordPress functions</strong> &#8211; Use WordPress APIs instead of raw PHP/SQL</li>
<li><strong>Not testing</strong> &#8211; Test across WordPress versions and with other plugins</li>
<li><strong>Poor error handling</strong> &#8211; Anticipate failures and handle gracefully</li>
</ul>
<h2 id="next-steps">Next Steps</h2>
<p>After mastering basics:</p>
<ol type="1">
<li>Study the Plugin Handbook thoroughly</li>
<li>Explore the Settings API for complex settings pages</li>
<li>Learn custom post types and taxonomies</li>
<li>Understand the REST API for modern integrations</li>
<li>Study established plugins’ code on GitHub</li>
</ol>
<h2 id="conclusion">Conclusion</h2>
<p>WordPress plugin development is accessible to beginners willing to learn. Start with simple plugins, follow security best practices, use WordPress APIs, and gradually build complexity. Your first plugin might be basic, but it establishes the foundation for building sophisticated WordPress extensions.</p>
<h2 id="external-links">External Links</h2>
<ol type="1">
<li><a href="https://developer.wordpress.org/plugins/">WordPress Plugin Handbook</a></li>
<li><a href="https://developer.wordpress.org/plugins/plugin-basics/">Plugin Basics</a></li>
<li><a href="https://localwp.com/">Local by Flywheel</a></li>
<li><a href="https://developer.wordpress.org/coding-standards/">WordPress Coding Standards</a></li>
<li><a href="https://developer.wordpress.org/">WordPress Developer Resources</a></li>
</ol>
<h2 id="call-to-action">Call to Action</h2>
<p>Supercharge your development! <a href="https://acfcopilotplugin.com/">ACF Copilot Pro</a> generates ACF field groups with AI, exports to PHP, and accelerates custom field workflows—try it free!</p>
<p>The post <a href="https://developryplugins.com/wordpress-plugin-development-from-scratch-complete-beginners-guide/">WordPress Plugin Development from Scratch: Complete Beginner&#8217;s Guide</a> appeared first on <a href="https://developryplugins.com">Developry Plugins</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>WordPress Plugin Internationalization: Making Plugins Translation-Ready</title>
		<link>https://developryplugins.com/wordpress-plugin-internationalization-making-plugins-translation-ready/</link>
		
		<dc:creator><![CDATA[Krasen Slavov]]></dc:creator>
		<pubDate>Sun, 25 Jan 2026 09:00:00 +0000</pubDate>
				<category><![CDATA[WordPress Plugin Development Guide]]></category>
		<category><![CDATA[i18n]]></category>
		<category><![CDATA[internationalization]]></category>
		<category><![CDATA[localization]]></category>
		<category><![CDATA[multilingual plugins]]></category>
		<category><![CDATA[translations]]></category>
		<guid isPermaLink="false">https://developryplugins.com/?p=172</guid>

					<description><![CDATA[<p>Making your WordPress plugin available in multiple languages opens doors to a global audience. Internationalization (i18n) prepares your code for translation, while localization (l10n) provides the actual translations. Let’s make...</p>
<p>The post <a href="https://developryplugins.com/wordpress-plugin-internationalization-making-plugins-translation-ready/">WordPress Plugin Internationalization: Making Plugins Translation-Ready</a> appeared first on <a href="https://developryplugins.com">Developry Plugins</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><!-- @format --></p>
<p>Making your WordPress plugin available in multiple languages opens doors to a global audience. Internationalization (i18n) prepares your code for translation, while localization (l10n) provides the actual translations. Let’s make your plugin speak every language.</p>
<h2 id="understanding-i18n-vs-l10n">Understanding i18n vs l10n</h2>
<p><strong>Internationalization (i18n):</strong> The process of preparing your plugin code to support multiple languages. This involves wrapping all user-facing strings in translation functions.</p>
<p><strong>Localization (l10n):</strong> The process of translating those strings into specific languages. Translators can work on l10n without touching your code.</p>
<p>The “18” in i18n represents the 18 letters between “i” and “n” in internationalization. Similarly, l10n has 10 letters between “l” and “n”.</p>
<h2 id="setting-up-your-text-domain">Setting Up Your Text Domain</h2>
<p>Every plugin needs a unique text domain identifier. Define it in your plugin header:</p>
<div class="sourceCode" id="cb1">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true"></a><span class="co">/**</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true"></a><span class="co"> * Plugin Name: My Awesome Plugin</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true"></a><span class="co"> * Text Domain: my-awesome-plugin</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true"></a><span class="co"> * Domain Path: /languages</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true"></a><span class="co"> */</span></span></code></pre>
</div>
<p>The text domain should match your plugin slug. Load translations in your main plugin file:</p>
<div class="sourceCode" id="cb2">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true"></a><span class="kw">function</span> myplugin_load_textdomain<span class="ot">()</span> {</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true"></a>    load_plugin_textdomain<span class="ot">(</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true"></a>        <span class="st">&#39;my-awesome-plugin&#39;</span><span class="ot">,</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true"></a>        <span class="kw">false</span><span class="ot">,</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true"></a>        <span class="fu">dirname</span><span class="ot">(</span>plugin_basename<span class="ot">(</span><span class="kw">__FILE__</span><span class="ot">))</span> . <span class="st">&#39;/languages&#39;</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true"></a>    <span class="ot">);</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true"></a>}</span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true"></a>add_action<span class="ot">(</span><span class="st">&#39;plugins_loaded&#39;</span><span class="ot">,</span> <span class="st">&#39;myplugin_load_textdomain&#39;</span><span class="ot">);</span></span></code></pre>
</div>
<p>This tells WordPress where to find translation files for your plugin.</p>
<h2 id="translation-functions">Translation Functions</h2>
<p>WordPress provides several functions for marking strings as translatable:</p>
<p><strong>Basic Translation:</strong></p>
<div class="sourceCode" id="cb3">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true"></a><span class="co">// Returns translated string</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true"></a><span class="kw">$text</span> = <span class="kw">__</span><span class="ot">(</span><span class="st">&#39;Hello World&#39;</span><span class="ot">,</span> <span class="st">&#39;my-awesome-plugin&#39;</span><span class="ot">);</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true"></a></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true"></a><span class="co">// Echoes translated string</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true"></a>_e<span class="ot">(</span><span class="st">&#39;Welcome to my plugin&#39;</span><span class="ot">,</span> <span class="st">&#39;my-awesome-plugin&#39;</span><span class="ot">);</span></span></code></pre>
</div>
<p><strong>Translation with Context:</strong></p>
<p>Use context when the same word might need different translations:</p>
<div class="sourceCode" id="cb4">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true"></a><span class="co">// &quot;Post&quot; as in a blog post</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true"></a><span class="kw">$label</span> = _x<span class="ot">(</span><span class="st">&#39;Post&#39;</span><span class="ot">,</span> <span class="st">&#39;noun&#39;</span><span class="ot">,</span> <span class="st">&#39;my-awesome-plugin&#39;</span><span class="ot">);</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true"></a></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true"></a><span class="co">// &quot;Post&quot; as in submit button</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true"></a><span class="kw">$button</span> = _x<span class="ot">(</span><span class="st">&#39;Post&#39;</span><span class="ot">,</span> <span class="st">&#39;verb&#39;</span><span class="ot">,</span> <span class="st">&#39;my-awesome-plugin&#39;</span><span class="ot">);</span></span></code></pre>
</div>
<p><strong>Plural Forms:</strong></p>
<p>Different languages handle plurals differently. Never hardcode plural logic:</p>
<div class="sourceCode" id="cb5">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true"></a><span class="co">// Wrong approach</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true"></a><span class="kw">$text</span> = <span class="kw">$count</span> . <span class="st">&#39; item&#39;</span><span class="ot">;</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true"></a><span class="kw">if</span> <span class="ot">(</span><span class="kw">$count</span> != <span class="dv">1</span><span class="ot">)</span> {</span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true"></a>    <span class="kw">$text</span> .= <span class="st">&#39;s&#39;</span><span class="ot">;</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true"></a>}</span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true"></a></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true"></a><span class="co">// Correct approach</span></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true"></a><span class="kw">$text</span> = <span class="fu">sprintf</span><span class="ot">(</span></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true"></a>    _n<span class="ot">(</span><span class="st">&#39;%d item&#39;</span><span class="ot">,</span> <span class="st">&#39;%d items&#39;</span><span class="ot">,</span> <span class="kw">$count</span><span class="ot">,</span> <span class="st">&#39;my-awesome-plugin&#39;</span><span class="ot">),</span></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true"></a>    <span class="kw">$count</span></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true"></a><span class="ot">);</span></span></code></pre>
</div>
<p><strong>Plural with Context:</strong></p>
<div class="sourceCode" id="cb6">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true"></a><span class="kw">$text</span> = <span class="fu">sprintf</span><span class="ot">(</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true"></a>    _nx<span class="ot">(</span><span class="st">&#39;%d product&#39;</span><span class="ot">,</span> <span class="st">&#39;%d products&#39;</span><span class="ot">,</span> <span class="kw">$count</span><span class="ot">,</span> <span class="st">&#39;e-commerce&#39;</span><span class="ot">,</span> <span class="st">&#39;my-awesome-plugin&#39;</span><span class="ot">),</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true"></a>    <span class="kw">$count</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true"></a><span class="ot">);</span></span></code></pre>
</div>
<h2 id="escaped-translation-functions">Escaped Translation Functions</h2>
<p>Always escape output for security. WordPress combines translation and escaping:</p>
<div class="sourceCode" id="cb7">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true"></a><span class="co">// For HTML content</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true"></a><span class="kw">echo</span> esc_html__<span class="ot">(</span><span class="st">&#39;Plugin Settings&#39;</span><span class="ot">,</span> <span class="st">&#39;my-awesome-plugin&#39;</span><span class="ot">);</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true"></a>esc_html_e<span class="ot">(</span><span class="st">&#39;Save Changes&#39;</span><span class="ot">,</span> <span class="st">&#39;my-awesome-plugin&#39;</span><span class="ot">);</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true"></a></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true"></a><span class="co">// For HTML attributes</span></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true"></a><span class="kw">$title</span> = esc_attr__<span class="ot">(</span><span class="st">&#39;Click to expand&#39;</span><span class="ot">,</span> <span class="st">&#39;my-awesome-plugin&#39;</span><span class="ot">);</span></span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true"></a><span class="kw">echo</span> <span class="st">&#39;&lt;div title=&quot;&#39;</span> . esc_attr__<span class="ot">(</span><span class="st">&#39;Help text&#39;</span><span class="ot">,</span> <span class="st">&#39;my-awesome-plugin&#39;</span><span class="ot">)</span> . <span class="st">&#39;&quot;&gt;&#39;</span><span class="ot">;</span></span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true"></a></span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true"></a><span class="co">// With context</span></span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true"></a><span class="kw">$label</span> = esc_html_x<span class="ot">(</span><span class="st">&#39;Post&#39;</span><span class="ot">,</span> <span class="st">&#39;noun&#39;</span><span class="ot">,</span> <span class="st">&#39;my-awesome-plugin&#39;</span><span class="ot">);</span></span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true"></a></span>
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true"></a><span class="co">// For admin pages</span></span>
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true"></a><span class="kw">echo</span> <span class="st">&#39;&lt;h1&gt;&#39;</span> . esc_html__<span class="ot">(</span><span class="st">&#39;Dashboard&#39;</span><span class="ot">,</span> <span class="st">&#39;my-awesome-plugin&#39;</span><span class="ot">)</span> . <span class="st">&#39;&lt;/h1&gt;&#39;</span><span class="ot">;</span></span></code></pre>
</div>
<h2 id="translating-dynamic-content">Translating Dynamic Content</h2>
<p>Use placeholders for variable content:</p>
<div class="sourceCode" id="cb8">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true"></a><span class="co">// Single placeholder</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true"></a><span class="kw">$message</span> = <span class="fu">sprintf</span><span class="ot">(</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true"></a>    <span class="kw">__</span><span class="ot">(</span><span class="st">&#39;Welcome, %s!&#39;</span><span class="ot">,</span> <span class="st">&#39;my-awesome-plugin&#39;</span><span class="ot">),</span></span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true"></a>    <span class="kw">$user_name</span></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true"></a><span class="ot">);</span></span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true"></a></span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true"></a><span class="co">// Multiple placeholders with ordering</span></span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true"></a><span class="kw">$message</span> = <span class="fu">sprintf</span><span class="ot">(</span></span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true"></a>    <span class="kw">__</span><span class="ot">(</span><span class="st">&#39;You have %1$d new messages and %2$d notifications&#39;</span><span class="ot">,</span> <span class="st">&#39;my-awesome-plugin&#39;</span><span class="ot">),</span></span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true"></a>    <span class="kw">$message_count</span><span class="ot">,</span></span>
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true"></a>    <span class="kw">$notification_count</span></span>
<span id="cb8-12"><a href="#cb8-12" aria-hidden="true"></a><span class="ot">);</span></span>
<span id="cb8-13"><a href="#cb8-13" aria-hidden="true"></a></span>
<span id="cb8-14"><a href="#cb8-14" aria-hidden="true"></a><span class="co">// Named placeholders (WordPress 5.5+)</span></span>
<span id="cb8-15"><a href="#cb8-15" aria-hidden="true"></a><span class="kw">$message</span> = <span class="fu">sprintf</span><span class="ot">(</span></span>
<span id="cb8-16"><a href="#cb8-16" aria-hidden="true"></a>    <span class="kw">__</span><span class="ot">(</span><span class="st">&#39;Hello %(name)s, you have %(count)d messages&#39;</span><span class="ot">,</span> <span class="st">&#39;my-awesome-plugin&#39;</span><span class="ot">),</span></span>
<span id="cb8-17"><a href="#cb8-17" aria-hidden="true"></a>    <span class="kw">array</span><span class="ot">(</span></span>
<span id="cb8-18"><a href="#cb8-18" aria-hidden="true"></a>        <span class="st">&#39;name&#39;</span> =&gt; <span class="kw">$user_name</span><span class="ot">,</span></span>
<span id="cb8-19"><a href="#cb8-19" aria-hidden="true"></a>        <span class="st">&#39;count&#39;</span> =&gt; <span class="kw">$message_count</span></span>
<span id="cb8-20"><a href="#cb8-20" aria-hidden="true"></a>    <span class="ot">)</span></span>
<span id="cb8-21"><a href="#cb8-21" aria-hidden="true"></a><span class="ot">);</span></span></code></pre>
</div>
<h2 id="translator-comments">Translator Comments</h2>
<p>Provide context for translators using special comments:</p>
<div class="sourceCode" id="cb9">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true"></a><span class="co">/* translators: %s: user display name */</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true"></a><span class="kw">$greeting</span> = <span class="fu">sprintf</span><span class="ot">(</span>__<span class="ot">(</span><span class="st">&#39;Welcome back, %s&#39;</span><span class="ot">,</span> <span class="st">&#39;my-awesome-plugin&#39;</span><span class="ot">),</span> <span class="kw">$name</span><span class="ot">);</span></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true"></a></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true"></a><span class="co">/* translators: 1: number of posts, 2: post type name */</span></span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true"></a><span class="kw">$status</span> = <span class="fu">sprintf</span><span class="ot">(</span></span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true"></a>    <span class="kw">__</span><span class="ot">(</span><span class="st">&#39;Found %1$d %2$s&#39;</span><span class="ot">,</span> <span class="st">&#39;my-awesome-plugin&#39;</span><span class="ot">),</span></span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true"></a>    <span class="kw">$count</span><span class="ot">,</span></span>
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true"></a>    <span class="kw">$post_type</span></span>
<span id="cb9-9"><a href="#cb9-9" aria-hidden="true"></a><span class="ot">);</span></span></code></pre>
</div>
<p>These comments appear in translation files, helping translators understand context.</p>
<h2 id="avoiding-common-mistakes">Avoiding Common Mistakes</h2>
<p><strong>Never concatenate translatable strings:</strong></p>
<div class="sourceCode" id="cb10">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true"></a><span class="co">// Wrong - breaks translation</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true"></a><span class="kw">echo</span> <span class="kw">__</span><span class="ot">(</span><span class="st">&#39;Click&#39;</span><span class="ot">,</span> <span class="st">&#39;my-awesome-plugin&#39;</span><span class="ot">)</span> . <span class="st">&#39; &#39;</span> .</span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true"></a>     <span class="st">&#39;&lt;a href=&quot;#&quot;&gt;&#39;</span> . <span class="kw">__</span><span class="ot">(</span><span class="st">&#39;here&#39;</span><span class="ot">,</span> <span class="st">&#39;my-awesome-plugin&#39;</span><span class="ot">)</span> . <span class="st">&#39;&lt;/a&gt;&#39;</span><span class="ot">;</span></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true"></a></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true"></a><span class="co">// Correct - translatable as complete phrase</span></span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true"></a><span class="fu">printf</span><span class="ot">(</span></span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true"></a>    <span class="kw">__</span><span class="ot">(</span><span class="st">&#39;Click &lt;a href=&quot;#&quot;&gt;here&lt;/a&gt;&#39;</span><span class="ot">,</span> <span class="st">&#39;my-awesome-plugin&#39;</span><span class="ot">)</span></span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true"></a><span class="ot">);</span></span></code></pre>
</div>
<p><strong>Don’t split sentences:</strong></p>
<div class="sourceCode" id="cb11">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true"></a><span class="co">// Wrong</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true"></a><span class="kw">echo</span> <span class="kw">__</span><span class="ot">(</span><span class="st">&#39;Total:&#39;</span><span class="ot">,</span> <span class="st">&#39;my-awesome-plugin&#39;</span><span class="ot">)</span> . <span class="st">&#39; &#39;</span> . <span class="kw">$count</span><span class="ot">;</span></span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true"></a></span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true"></a><span class="co">// Correct</span></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true"></a><span class="fu">printf</span><span class="ot">(</span>__<span class="ot">(</span><span class="st">&#39;Total: %d&#39;</span><span class="ot">,</span> <span class="st">&#39;my-awesome-plugin&#39;</span><span class="ot">),</span> <span class="kw">$count</span><span class="ot">);</span></span></code></pre>
</div>
<p><strong>Avoid hardcoded HTML in translations:</strong></p>
<div class="sourceCode" id="cb12">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true"></a><span class="co">// Wrong</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true"></a><span class="kw">__</span><span class="ot">(</span><span class="st">&#39;&lt;strong&gt;Important:&lt;/strong&gt; Save your work&#39;</span><span class="ot">,</span> <span class="st">&#39;my-awesome-plugin&#39;</span><span class="ot">);</span></span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true"></a></span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true"></a><span class="co">// Better - allows translator to reorder</span></span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true"></a><span class="fu">sprintf</span><span class="ot">(</span></span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true"></a>    <span class="kw">__</span><span class="ot">(</span><span class="st">&#39;%sImportant:%s Save your work&#39;</span><span class="ot">,</span> <span class="st">&#39;my-awesome-plugin&#39;</span><span class="ot">),</span></span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true"></a>    <span class="st">&#39;&lt;strong&gt;&#39;</span><span class="ot">,</span></span>
<span id="cb12-8"><a href="#cb12-8" aria-hidden="true"></a>    <span class="st">&#39;&lt;/strong&gt;&#39;</span></span>
<span id="cb12-9"><a href="#cb12-9" aria-hidden="true"></a><span class="ot">);</span></span></code></pre>
</div>
<h2 id="creating-pot-files">Creating POT Files</h2>
<p>POT (Portable Object Template) files contain all translatable strings from your plugin. Generate them using WP-CLI:</p>
<div class="sourceCode" id="cb13">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true"></a><span class="ex">wp</span> i18n make-pot . languages/my-awesome-plugin.pot</span></code></pre>
</div>
<p>Or use Poedit to scan your plugin directory. The POT file serves as the template for all language translations.</p>
<h2 id="translation-file-structure">Translation File Structure</h2>
<p>Organize translation files in a <code>languages/</code> directory:</p>
<pre><code>my-awesome-plugin/
├── languages/
│   ├── my-awesome-plugin.pot        (template)
│   ├── my-awesome-plugin-es_ES.po   (Spanish - editable)
│   ├── my-awesome-plugin-es_ES.mo   (Spanish - compiled)
│   ├── my-awesome-plugin-fr_FR.po   (French - editable)
│   └── my-awesome-plugin-fr_FR.mo   (French - compiled)</code></pre>
<p>PO files contain the actual translations. MO files are compiled binary versions that WordPress loads.</p>
<h2 id="creating-translations-with-poedit">Creating Translations with Poedit</h2>
<ol type="1">
<li>Open Poedit and select “Create New Translation”</li>
<li>Choose your POT file as the source</li>
<li>Select target language</li>
<li>Translate strings in the interface</li>
<li>Save as <code>.po</code> file (Poedit auto-generates <code>.mo</code>)</li>
</ol>
<p>Poedit provides context from translator comments and shows source code references.</p>
<h2 id="javascript-internationalization">JavaScript Internationalization</h2>
<p>Translate strings in JavaScript files:</p>
<div class="sourceCode" id="cb15">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true"></a><span class="co">// Enqueue script and make it translatable</span></span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true"></a><span class="kw">function</span> myplugin_enqueue_scripts<span class="ot">()</span> {</span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true"></a>    wp_enqueue_script<span class="ot">(</span></span>
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true"></a>        <span class="st">&#39;myplugin-frontend&#39;</span><span class="ot">,</span></span>
<span id="cb15-5"><a href="#cb15-5" aria-hidden="true"></a>        plugins_url<span class="ot">(</span><span class="st">&#39;js/frontend.js&#39;</span><span class="ot">,</span> <span class="kw">__FILE__</span><span class="ot">),</span></span>
<span id="cb15-6"><a href="#cb15-6" aria-hidden="true"></a>        <span class="kw">array</span><span class="ot">(</span><span class="st">&#39;jquery&#39;</span><span class="ot">),</span></span>
<span id="cb15-7"><a href="#cb15-7" aria-hidden="true"></a>        <span class="st">&#39;1.0&#39;</span><span class="ot">,</span></span>
<span id="cb15-8"><a href="#cb15-8" aria-hidden="true"></a>        <span class="kw">true</span></span>
<span id="cb15-9"><a href="#cb15-9" aria-hidden="true"></a>    <span class="ot">);</span></span>
<span id="cb15-10"><a href="#cb15-10" aria-hidden="true"></a></span>
<span id="cb15-11"><a href="#cb15-11" aria-hidden="true"></a>    wp_set_script_translations<span class="ot">(</span></span>
<span id="cb15-12"><a href="#cb15-12" aria-hidden="true"></a>        <span class="st">&#39;myplugin-frontend&#39;</span><span class="ot">,</span></span>
<span id="cb15-13"><a href="#cb15-13" aria-hidden="true"></a>        <span class="st">&#39;my-awesome-plugin&#39;</span><span class="ot">,</span></span>
<span id="cb15-14"><a href="#cb15-14" aria-hidden="true"></a>        plugin_dir_path<span class="ot">(</span><span class="kw">__FILE__</span><span class="ot">)</span> . <span class="st">&#39;languages&#39;</span></span>
<span id="cb15-15"><a href="#cb15-15" aria-hidden="true"></a>    <span class="ot">);</span></span>
<span id="cb15-16"><a href="#cb15-16" aria-hidden="true"></a>}</span>
<span id="cb15-17"><a href="#cb15-17" aria-hidden="true"></a>add_action<span class="ot">(</span><span class="st">&#39;wp_enqueue_scripts&#39;</span><span class="ot">,</span> <span class="st">&#39;myplugin_enqueue_scripts&#39;</span><span class="ot">);</span></span></code></pre>
</div>
<p>In your JavaScript file:</p>
<div class="sourceCode" id="cb16">
<pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true"></a><span class="kw">const</span> { __ } <span class="op">=</span> wp<span class="op">.</span><span class="at">i18n</span><span class="op">;</span></span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true"></a></span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true"></a><span class="co">// Simple translation</span></span>
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true"></a><span class="kw">const</span> message <span class="op">=</span> <span class="fu">__</span>(<span class="st">&quot;Click to continue&quot;</span><span class="op">,</span> <span class="st">&quot;my-awesome-plugin&quot;</span>)<span class="op">;</span></span>
<span id="cb16-5"><a href="#cb16-5" aria-hidden="true"></a></span>
<span id="cb16-6"><a href="#cb16-6" aria-hidden="true"></a><span class="co">// With sprintf</span></span>
<span id="cb16-7"><a href="#cb16-7" aria-hidden="true"></a><span class="kw">const</span> greeting <span class="op">=</span> <span class="fu">sprintf</span>(<span class="fu">__</span>(<span class="st">&quot;Welcome, %s!&quot;</span><span class="op">,</span> <span class="st">&quot;my-awesome-plugin&quot;</span>)<span class="op">,</span> userName)<span class="op">;</span></span></code></pre>
</div>
<p>Generate JSON files for JavaScript translations:</p>
<div class="sourceCode" id="cb17">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true"></a><span class="ex">wp</span> i18n make-json languages --no-purge</span></code></pre>
</div>
<h2 id="date-and-time-localization">Date and Time Localization</h2>
<p>Use WordPress date functions for proper localization:</p>
<div class="sourceCode" id="cb18">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true"></a><span class="co">// WordPress handles localization automatically</span></span>
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true"></a><span class="kw">echo</span> date_i18n<span class="ot">(</span>get_option<span class="ot">(</span><span class="st">&#39;date_format&#39;</span><span class="ot">),</span> <span class="fu">strtotime</span><span class="ot">(</span><span class="kw">$date</span><span class="ot">));</span></span>
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true"></a></span>
<span id="cb18-4"><a href="#cb18-4" aria-hidden="true"></a><span class="co">// Time format</span></span>
<span id="cb18-5"><a href="#cb18-5" aria-hidden="true"></a><span class="kw">echo</span> date_i18n<span class="ot">(</span>get_option<span class="ot">(</span><span class="st">&#39;time_format&#39;</span><span class="ot">),</span> current_time<span class="ot">(</span><span class="st">&#39;timestamp&#39;</span><span class="ot">));</span></span>
<span id="cb18-6"><a href="#cb18-6" aria-hidden="true"></a></span>
<span id="cb18-7"><a href="#cb18-7" aria-hidden="true"></a><span class="co">// Custom format</span></span>
<span id="cb18-8"><a href="#cb18-8" aria-hidden="true"></a><span class="kw">echo</span> date_i18n<span class="ot">(</span><span class="st">&#39;F j, Y&#39;</span><span class="ot">,</span> <span class="fu">strtotime</span><span class="ot">(</span><span class="kw">$date</span><span class="ot">));</span></span></code></pre>
</div>
<h2 id="number-formatting">Number Formatting</h2>
<p>Use <code>number_format_i18n()</code> for locale-aware number formatting:</p>
<div class="sourceCode" id="cb19">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true"></a><span class="co">// Formats according to user&#39;s locale</span></span>
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true"></a><span class="kw">echo</span> number_format_i18n<span class="ot">(</span><span class="fl">1234.56</span><span class="ot">);</span> <span class="co">// 1,234.56 in English, 1.234,56 in German</span></span>
<span id="cb19-3"><a href="#cb19-3" aria-hidden="true"></a></span>
<span id="cb19-4"><a href="#cb19-4" aria-hidden="true"></a><span class="co">// With specific decimals</span></span>
<span id="cb19-5"><a href="#cb19-5" aria-hidden="true"></a><span class="kw">echo</span> number_format_i18n<span class="ot">(</span><span class="fl">1234.56789</span><span class="ot">,</span> <span class="dv">2</span><span class="ot">);</span> <span class="co">// 1,234.57</span></span></code></pre>
</div>
<h2 id="rtl-language-support">RTL Language Support</h2>
<p>Add RTL stylesheet support for languages like Arabic and Hebrew:</p>
<div class="sourceCode" id="cb20">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true"></a><span class="kw">function</span> myplugin_enqueue_styles<span class="ot">()</span> {</span>
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true"></a>    wp_enqueue_style<span class="ot">(</span><span class="st">&#39;myplugin-style&#39;</span><span class="ot">,</span></span>
<span id="cb20-3"><a href="#cb20-3" aria-hidden="true"></a>        plugins_url<span class="ot">(</span><span class="st">&#39;css/style.css&#39;</span><span class="ot">,</span> <span class="kw">__FILE__</span><span class="ot">));</span></span>
<span id="cb20-4"><a href="#cb20-4" aria-hidden="true"></a></span>
<span id="cb20-5"><a href="#cb20-5" aria-hidden="true"></a>    <span class="co">// Load RTL stylesheet if needed</span></span>
<span id="cb20-6"><a href="#cb20-6" aria-hidden="true"></a>    <span class="kw">if</span> <span class="ot">(</span>is_rtl<span class="ot">())</span> {</span>
<span id="cb20-7"><a href="#cb20-7" aria-hidden="true"></a>        wp_enqueue_style<span class="ot">(</span><span class="st">&#39;myplugin-rtl&#39;</span><span class="ot">,</span></span>
<span id="cb20-8"><a href="#cb20-8" aria-hidden="true"></a>            plugins_url<span class="ot">(</span><span class="st">&#39;css/rtl.css&#39;</span><span class="ot">,</span> <span class="kw">__FILE__</span><span class="ot">));</span></span>
<span id="cb20-9"><a href="#cb20-9" aria-hidden="true"></a>    }</span>
<span id="cb20-10"><a href="#cb20-10" aria-hidden="true"></a>}</span>
<span id="cb20-11"><a href="#cb20-11" aria-hidden="true"></a>add_action<span class="ot">(</span><span class="st">&#39;wp_enqueue_scripts&#39;</span><span class="ot">,</span> <span class="st">&#39;myplugin_enqueue_styles&#39;</span><span class="ot">);</span></span></code></pre>
</div>
<p>Or use automatic RTL generation:</p>
<div class="sourceCode" id="cb21">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true"></a>wp_style_add_data<span class="ot">(</span><span class="st">&#39;myplugin-style&#39;</span><span class="ot">,</span> <span class="st">&#39;rtl&#39;</span><span class="ot">,</span> <span class="st">&#39;replace&#39;</span><span class="ot">);</span></span></code></pre>
</div>
<h2 id="testing-translations">Testing Translations</h2>
<p>Test your plugin in different languages:</p>
<ol type="1">
<li>Change WordPress language in Settings &gt; General</li>
<li>Install a language pack from Settings &gt; General</li>
<li>Use a plugin like Loco Translate for quick testing</li>
<li>Verify all strings are translatable</li>
<li>Check RTL layout if supporting those languages</li>
</ol>
<h2 id="wordpress.org-translation-platform">WordPress.org Translation Platform</h2>
<p>For plugins hosted on WordPress.org:</p>
<ol type="1">
<li>Submit your plugin with POT file included</li>
<li>Translators use translate.wordpress.org</li>
<li>Language packs are automatically generated</li>
<li>Users receive translations through WordPress updates</li>
</ol>
<p>Contributors can help translate at: <code>https://translate.wordpress.org/projects/wp-plugins/your-plugin-slug</code></p>
<h2 id="translation-workflow-best-practices">Translation Workflow Best Practices</h2>
<p><strong>During Development:</strong></p>
<ul>
<li>Wrap all user-facing strings immediately</li>
<li>Use consistent text domain throughout</li>
<li>Add translator comments for context</li>
<li>Never use variables as text domain</li>
</ul>
<p><strong>Before Release:</strong></p>
<ul>
<li>Generate POT file</li>
<li>Test with at least one translation</li>
<li>Verify all strings are translatable</li>
<li>Check for missing text domains</li>
</ul>
<p><strong>After Release:</strong></p>
<ul>
<li>Update POT file with each version</li>
<li>Notify translators of new strings</li>
<li>Keep existing string IDs stable</li>
<li>Provide context for new additions</li>
</ul>
<h2 id="performance-considerations">Performance Considerations</h2>
<p>Translation loading has minimal performance impact when done correctly:</p>
<div class="sourceCode" id="cb22">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true"></a><span class="co">// Load translations only when needed</span></span>
<span id="cb22-2"><a href="#cb22-2" aria-hidden="true"></a><span class="kw">if</span> <span class="ot">(</span>is_admin<span class="ot">())</span> {</span>
<span id="cb22-3"><a href="#cb22-3" aria-hidden="true"></a>    load_plugin_textdomain<span class="ot">(</span><span class="st">&#39;my-awesome-plugin&#39;</span><span class="ot">,</span> <span class="kw">false</span><span class="ot">,</span> <span class="fu">dirname</span><span class="ot">(</span>plugin_basename<span class="ot">(</span><span class="kw">__FILE__</span><span class="ot">))</span> . <span class="st">&#39;/languages&#39;</span><span class="ot">);</span></span>
<span id="cb22-4"><a href="#cb22-4" aria-hidden="true"></a>}</span>
<span id="cb22-5"><a href="#cb22-5" aria-hidden="true"></a></span>
<span id="cb22-6"><a href="#cb22-6" aria-hidden="true"></a><span class="co">// Cache expensive translations</span></span>
<span id="cb22-7"><a href="#cb22-7" aria-hidden="true"></a><span class="kw">$translated</span> = wp_cache_get<span class="ot">(</span><span class="st">&#39;myplugin_cached_string&#39;</span><span class="ot">,</span> <span class="st">&#39;myplugin&#39;</span><span class="ot">);</span></span>
<span id="cb22-8"><a href="#cb22-8" aria-hidden="true"></a><span class="kw">if</span> <span class="ot">(</span><span class="kw">false</span> === <span class="kw">$translated</span><span class="ot">)</span> {</span>
<span id="cb22-9"><a href="#cb22-9" aria-hidden="true"></a>    <span class="kw">$translated</span> = <span class="kw">__</span><span class="ot">(</span><span class="st">&#39;Expensive string&#39;</span><span class="ot">,</span> <span class="st">&#39;my-awesome-plugin&#39;</span><span class="ot">);</span></span>
<span id="cb22-10"><a href="#cb22-10" aria-hidden="true"></a>    wp_cache_set<span class="ot">(</span><span class="st">&#39;myplugin_cached_string&#39;</span><span class="ot">,</span> <span class="kw">$translated</span><span class="ot">,</span> <span class="st">&#39;myplugin&#39;</span><span class="ot">,</span> <span class="kw">HOUR_IN_SECONDS</span><span class="ot">);</span></span>
<span id="cb22-11"><a href="#cb22-11" aria-hidden="true"></a>}</span></code></pre>
</div>
<h2 id="complete-example">Complete Example</h2>
<p>Here’s a complete translatable plugin snippet:</p>
<div class="sourceCode" id="cb23">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true"></a><span class="kw">function</span> myplugin_display_stats<span class="ot">(</span><span class="kw">$user_id</span><span class="ot">)</span> {</span>
<span id="cb23-2"><a href="#cb23-2" aria-hidden="true"></a>    <span class="kw">$posts</span> = count_user_posts<span class="ot">(</span><span class="kw">$user_id</span><span class="ot">);</span></span>
<span id="cb23-3"><a href="#cb23-3" aria-hidden="true"></a>    <span class="kw">$comments</span> = get_comments<span class="ot">(</span><span class="kw">array</span><span class="ot">(</span><span class="st">&#39;user_id&#39;</span> =&gt; <span class="kw">$user_id</span><span class="ot">,</span> <span class="st">&#39;count&#39;</span> =&gt; <span class="kw">true</span><span class="ot">));</span></span>
<span id="cb23-4"><a href="#cb23-4" aria-hidden="true"></a></span>
<span id="cb23-5"><a href="#cb23-5" aria-hidden="true"></a>    <span class="kw">$output</span> = <span class="st">&#39;&lt;div class=&quot;user-stats&quot;&gt;&#39;</span><span class="ot">;</span></span>
<span id="cb23-6"><a href="#cb23-6" aria-hidden="true"></a></span>
<span id="cb23-7"><a href="#cb23-7" aria-hidden="true"></a>    <span class="co">/* translators: %s: user display name */</span></span>
<span id="cb23-8"><a href="#cb23-8" aria-hidden="true"></a>    <span class="kw">$output</span> .= <span class="st">&#39;&lt;h3&gt;&#39;</span> . <span class="fu">sprintf</span><span class="ot">(</span></span>
<span id="cb23-9"><a href="#cb23-9" aria-hidden="true"></a>        esc_html__<span class="ot">(</span><span class="st">&#39;Statistics for %s&#39;</span><span class="ot">,</span> <span class="st">&#39;my-awesome-plugin&#39;</span><span class="ot">),</span></span>
<span id="cb23-10"><a href="#cb23-10" aria-hidden="true"></a>        get_userdata<span class="ot">(</span><span class="kw">$user_id</span><span class="ot">)</span>-&gt;display_name</span>
<span id="cb23-11"><a href="#cb23-11" aria-hidden="true"></a>    <span class="ot">)</span> . <span class="st">&#39;&lt;/h3&gt;&#39;</span><span class="ot">;</span></span>
<span id="cb23-12"><a href="#cb23-12" aria-hidden="true"></a></span>
<span id="cb23-13"><a href="#cb23-13" aria-hidden="true"></a>    <span class="co">/* translators: %d: number of posts */</span></span>
<span id="cb23-14"><a href="#cb23-14" aria-hidden="true"></a>    <span class="kw">$output</span> .= <span class="st">&#39;&lt;p&gt;&#39;</span> . <span class="fu">sprintf</span><span class="ot">(</span></span>
<span id="cb23-15"><a href="#cb23-15" aria-hidden="true"></a>        esc_html<span class="ot">(</span>_n<span class="ot">(</span><span class="st">&#39;%d post published&#39;</span><span class="ot">,</span> <span class="st">&#39;%d posts published&#39;</span><span class="ot">,</span> <span class="kw">$posts</span><span class="ot">,</span> <span class="st">&#39;my-awesome-plugin&#39;</span><span class="ot">)),</span></span>
<span id="cb23-16"><a href="#cb23-16" aria-hidden="true"></a>        number_format_i18n<span class="ot">(</span><span class="kw">$posts</span><span class="ot">)</span></span>
<span id="cb23-17"><a href="#cb23-17" aria-hidden="true"></a>    <span class="ot">)</span> . <span class="st">&#39;&lt;/p&gt;&#39;</span><span class="ot">;</span></span>
<span id="cb23-18"><a href="#cb23-18" aria-hidden="true"></a></span>
<span id="cb23-19"><a href="#cb23-19" aria-hidden="true"></a>    <span class="co">/* translators: %d: number of comments */</span></span>
<span id="cb23-20"><a href="#cb23-20" aria-hidden="true"></a>    <span class="kw">$output</span> .= <span class="st">&#39;&lt;p&gt;&#39;</span> . <span class="fu">sprintf</span><span class="ot">(</span></span>
<span id="cb23-21"><a href="#cb23-21" aria-hidden="true"></a>        esc_html<span class="ot">(</span>_n<span class="ot">(</span><span class="st">&#39;%d comment written&#39;</span><span class="ot">,</span> <span class="st">&#39;%d comments written&#39;</span><span class="ot">,</span> <span class="kw">$comments</span><span class="ot">,</span> <span class="st">&#39;my-awesome-plugin&#39;</span><span class="ot">)),</span></span>
<span id="cb23-22"><a href="#cb23-22" aria-hidden="true"></a>        number_format_i18n<span class="ot">(</span><span class="kw">$comments</span><span class="ot">)</span></span>
<span id="cb23-23"><a href="#cb23-23" aria-hidden="true"></a>    <span class="ot">)</span> . <span class="st">&#39;&lt;/p&gt;&#39;</span><span class="ot">;</span></span>
<span id="cb23-24"><a href="#cb23-24" aria-hidden="true"></a></span>
<span id="cb23-25"><a href="#cb23-25" aria-hidden="true"></a>    <span class="kw">$output</span> .= <span class="st">&#39;&lt;/div&gt;&#39;</span><span class="ot">;</span></span>
<span id="cb23-26"><a href="#cb23-26" aria-hidden="true"></a></span>
<span id="cb23-27"><a href="#cb23-27" aria-hidden="true"></a>    <span class="kw">return</span> <span class="kw">$output</span><span class="ot">;</span></span>
<span id="cb23-28"><a href="#cb23-28" aria-hidden="true"></a>}</span></code></pre>
</div>
<p>Internationalization ensures your plugin reaches users worldwide. With proper i18n implementation, translators can easily localize your plugin without modifying a single line of code.</p>
<h2 id="external-links">External Links</h2>
<ol type="1">
<li><a href="https://developer.wordpress.org/apis/internationalization/">Internationalization Documentation</a></li>
<li><a href="https://developer.wordpress.org/plugins/internationalization/">I18n for WordPress Developers</a></li>
<li><a href="https://translate.wordpress.org/">translate.wordpress.org</a></li>
<li><a href="https://poedit.net/">Poedit Translation Tool</a></li>
<li><a href="https://developer.wordpress.org/cli/commands/i18n/">WP-CLI i18n Commands</a></li>
</ol>
<h2 id="call-to-action">Call to Action</h2>
<p>Supercharge your development! <a href="https://acfcopilotplugin.com/">ACF Copilot Pro</a> generates ACF field groups with AI, exports to PHP, and accelerates custom field workflows—try it free!</p>
<p>The post <a href="https://developryplugins.com/wordpress-plugin-internationalization-making-plugins-translation-ready/">WordPress Plugin Internationalization: Making Plugins Translation-Ready</a> appeared first on <a href="https://developryplugins.com">Developry Plugins</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>WordPress Plugin Security Best Practices: Prevent Common Vulnerabilities</title>
		<link>https://developryplugins.com/wordpress-plugin-security-best-practices-prevent-common-vulnerabilities/</link>
		
		<dc:creator><![CDATA[Krasen Slavov]]></dc:creator>
		<pubDate>Tue, 20 Jan 2026 09:00:00 +0000</pubDate>
				<category><![CDATA[WordPress Plugin Development Guide]]></category>
		<category><![CDATA[plugin security]]></category>
		<category><![CDATA[security best practices]]></category>
		<category><![CDATA[sql injection]]></category>
		<category><![CDATA[wordpress security]]></category>
		<category><![CDATA[xss prevention]]></category>
		<guid isPermaLink="false">https://developryplugins.com/?p=173</guid>

					<description><![CDATA[<p>Plugin security vulnerabilities endanger millions of WordPress sites. As a plugin developer, you’re responsible for protecting user data, preventing attacks, and maintaining WordPress ecosystem trust. This guide covers essential security...</p>
<p>The post <a href="https://developryplugins.com/wordpress-plugin-security-best-practices-prevent-common-vulnerabilities/">WordPress Plugin Security Best Practices: Prevent Common Vulnerabilities</a> appeared first on <a href="https://developryplugins.com">Developry Plugins</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><!-- @format --></p>
<p>Plugin security vulnerabilities endanger millions of WordPress sites. As a plugin developer, you’re responsible for protecting user data, preventing attacks, and maintaining WordPress ecosystem trust. This guide covers essential security practices every plugin developer must follow.</p>
<h2 id="why-plugin-security-matters">Why Plugin Security Matters</h2>
<p>WordPress powers 43% of websites globally. Attackers target plugins because:</p>
<ul>
<li>Plugins often handle sensitive data</li>
<li>Security vulnerabilities affect thousands of sites using the same plugin</li>
<li>Poorly coded plugins provide entry points to otherwise secure sites</li>
<li>Plugin vulnerabilities appear in major security databases</li>
</ul>
<p>A single SQL injection vulnerability in your plugin could compromise thousands of WordPress installations. Security isn’t optional—it’s your primary responsibility as a developer.</p>
<h2 id="cross-site-scripting-xss-prevention">Cross-Site Scripting (XSS) Prevention</h2>
<p>XSS attacks inject malicious JavaScript into pages, stealing cookies, redirecting users, or modifying content.</p>
<p><strong>Always escape output:</strong></p>
<div class="sourceCode" id="cb1">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true"></a><span class="co">// HTML context</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true"></a><span class="kw">echo</span> <span class="st">&#39;&lt;p&gt;&#39;</span> . esc_html<span class="ot">(</span> <span class="kw">$user_input</span> <span class="ot">)</span> . <span class="st">&#39;&lt;/p&gt;&#39;</span><span class="ot">;</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true"></a></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true"></a><span class="co">// Attribute context</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true"></a><span class="kw">echo</span> <span class="st">&#39;&lt;div class=&quot;&#39;</span> . esc_attr<span class="ot">(</span> <span class="kw">$class_name</span> <span class="ot">)</span> . <span class="st">&#39;&quot;&gt;&#39;</span><span class="ot">;</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true"></a></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true"></a><span class="co">// URL context</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true"></a><span class="kw">echo</span> <span class="st">&#39;&lt;a href=&quot;&#39;</span> . esc_url<span class="ot">(</span> <span class="kw">$link</span> <span class="ot">)</span> . <span class="st">&#39;&quot;&gt;Link&lt;/a&gt;&#39;</span><span class="ot">;</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true"></a></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true"></a><span class="co">// JavaScript context</span></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true"></a><span class="kw">echo</span> <span class="st">&#39;&lt;script&gt;var data = &#39;</span> . esc_js<span class="ot">(</span> <span class="kw">$data</span> <span class="ot">)</span> . <span class="st">&#39;;&lt;/script&gt;&#39;</span><span class="ot">;</span></span></code></pre>
</div>
<p><strong>Allow specific HTML with wp_kses():</strong></p>
<div class="sourceCode" id="cb2">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true"></a><span class="kw">$allowed_html</span> = <span class="kw">array</span><span class="ot">(</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true"></a>    <span class="st">&#39;a&#39;</span> =&gt; <span class="kw">array</span><span class="ot">(</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true"></a>        <span class="st">&#39;href&#39;</span> =&gt; <span class="kw">array</span><span class="ot">(),</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true"></a>        <span class="st">&#39;title&#39;</span> =&gt; <span class="kw">array</span><span class="ot">()</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true"></a>    <span class="ot">),</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true"></a>    <span class="st">&#39;strong&#39;</span> =&gt; <span class="kw">array</span><span class="ot">(),</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true"></a>    <span class="st">&#39;em&#39;</span> =&gt; <span class="kw">array</span><span class="ot">()</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true"></a><span class="ot">);</span></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true"></a><span class="kw">echo</span> wp_kses<span class="ot">(</span> <span class="kw">$user_content</span><span class="ot">,</span> <span class="kw">$allowed_html</span> <span class="ot">);</span></span></code></pre>
</div>
<p>Never output unescaped user data. Ever.</p>
<h2 id="sql-injection-prevention">SQL Injection Prevention</h2>
<p>SQL injection allows attackers to execute arbitrary database queries.</p>
<p><strong>Always use prepared statements:</strong></p>
<div class="sourceCode" id="cb3">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true"></a><span class="kw">global</span> <span class="kw">$wpdb</span><span class="ot">;</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true"></a></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true"></a><span class="co">// WRONG - vulnerable to SQL injection</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true"></a><span class="kw">$user_id</span> = <span class="kw">$_GET</span><span class="ot">[</span><span class="st">&#39;user_id&#39;</span><span class="ot">];</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true"></a><span class="kw">$results</span> = <span class="kw">$wpdb</span>-&gt;get_results<span class="ot">(</span> <span class="st">&quot;SELECT * FROM </span><span class="kw">{$wpdb-&gt;users}</span><span class="st"> WHERE ID = </span><span class="kw">$user_id</span><span class="st">&quot;</span> <span class="ot">);</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true"></a></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true"></a><span class="co">// CORRECT - using prepare()</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true"></a><span class="kw">$user_id</span> = <span class="fu">intval</span><span class="ot">(</span> <span class="kw">$_GET</span><span class="ot">[</span><span class="st">&#39;user_id&#39;</span><span class="ot">]</span> <span class="ot">);</span></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true"></a><span class="kw">$results</span> = <span class="kw">$wpdb</span>-&gt;get_results<span class="ot">(</span></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true"></a>    <span class="kw">$wpdb</span>-&gt;prepare<span class="ot">(</span></span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true"></a>        <span class="st">&quot;SELECT * FROM </span><span class="kw">{$wpdb-&gt;users}</span><span class="st"> WHERE ID = %d&quot;</span><span class="ot">,</span></span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true"></a>        <span class="kw">$user_id</span></span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true"></a>    <span class="ot">)</span></span>
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true"></a><span class="ot">);</span></span></code></pre>
</div>
<p><strong>Placeholders for different data types:</strong></p>
<div class="sourceCode" id="cb4">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true"></a><span class="co">// %d for integers</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true"></a><span class="kw">$wpdb</span>-&gt;prepare<span class="ot">(</span> <span class="st">&quot;SELECT * FROM table WHERE id = %d&quot;</span><span class="ot">,</span> <span class="kw">$id</span> <span class="ot">);</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true"></a></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true"></a><span class="co">// %s for strings</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true"></a><span class="kw">$wpdb</span>-&gt;prepare<span class="ot">(</span> <span class="st">&quot;SELECT * FROM table WHERE name = %s&quot;</span><span class="ot">,</span> <span class="kw">$name</span> <span class="ot">);</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true"></a></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true"></a><span class="co">// %f for floats</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true"></a><span class="kw">$wpdb</span>-&gt;prepare<span class="ot">(</span> <span class="st">&quot;SELECT * FROM table WHERE price = %f&quot;</span><span class="ot">,</span> <span class="kw">$price</span> <span class="ot">);</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true"></a></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true"></a><span class="co">// Multiple values</span></span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true"></a><span class="kw">$wpdb</span>-&gt;prepare<span class="ot">(</span></span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true"></a>    <span class="st">&quot;SELECT * FROM table WHERE id = %d AND name = %s&quot;</span><span class="ot">,</span></span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true"></a>    <span class="kw">$id</span><span class="ot">,</span></span>
<span id="cb4-14"><a href="#cb4-14" aria-hidden="true"></a>    <span class="kw">$name</span></span>
<span id="cb4-15"><a href="#cb4-15" aria-hidden="true"></a><span class="ot">);</span></span></code></pre>
</div>
<p>Never concatenate variables into SQL queries.</p>
<h2 id="csrf-protection-with-nonces">CSRF Protection with Nonces</h2>
<p>CSRF attacks trick users into executing unwanted actions.</p>
<p><strong>Create nonces in forms:</strong></p>
<div class="sourceCode" id="cb5">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true"></a>&lt;form method=<span class="st">&quot;post&quot;</span>&gt;</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true"></a>    &lt;<span class="ot">?</span>php wp_nonce_field<span class="ot">(</span> <span class="st">&#39;dprt_save_settings&#39;</span><span class="ot">,</span> <span class="st">&#39;dprt_nonce&#39;</span> <span class="ot">);</span> <span class="kw">?&gt;</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true"></a>    &lt;input type=<span class="st">&quot;text&quot;</span> name=<span class="st">&quot;setting_value&quot;</span>&gt;</span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true"></a>    &lt;input type=<span class="st">&quot;submit&quot;</span> value=<span class="st">&quot;Save&quot;</span>&gt;</span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true"></a>&lt;/form&gt;</span></code></pre>
</div>
<p><strong>Verify nonces before processing:</strong></p>
<div class="sourceCode" id="cb6">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true"></a><span class="kw">if</span> <span class="ot">(</span> <span class="kw">isset</span><span class="ot">(</span> <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;submit&#39;</span><span class="ot">]</span> <span class="ot">)</span> <span class="ot">)</span> {</span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true"></a>    <span class="kw">if</span> <span class="ot">(</span> ! <span class="kw">isset</span><span class="ot">(</span> <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;dprt_nonce&#39;</span><span class="ot">]</span> <span class="ot">)</span> || ! wp_verify_nonce<span class="ot">(</span> <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;dprt_nonce&#39;</span><span class="ot">],</span> <span class="st">&#39;dprt_save_settings&#39;</span> <span class="ot">)</span> <span class="ot">)</span> {</span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true"></a>        wp_die<span class="ot">(</span> <span class="st">&#39;Security check failed&#39;</span> <span class="ot">);</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true"></a>    }</span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true"></a></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true"></a>    <span class="co">// Process form data</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true"></a>}</span></code></pre>
</div>
<p><strong>For admin pages, use check_admin_referer():</strong></p>
<div class="sourceCode" id="cb7">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true"></a>check_admin_referer<span class="ot">(</span> <span class="st">&#39;dprt_save_settings&#39;</span><span class="ot">,</span> <span class="st">&#39;dprt_nonce&#39;</span> <span class="ot">);</span></span></code></pre>
</div>
<p><strong>For AJAX:</strong></p>
<div class="sourceCode" id="cb8">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true"></a><span class="co">// JavaScript</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true"></a>$.post<span class="ot">(</span> ajaxurl<span class="ot">,</span> {</span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true"></a>    action: <span class="st">&#39;dprt_save&#39;</span><span class="ot">,</span></span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true"></a>    nonce: dprt_ajax.nonce<span class="ot">,</span></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true"></a>    data: formData</span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true"></a>}<span class="ot">);</span></span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true"></a></span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true"></a><span class="co">// PHP</span></span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true"></a><span class="kw">function</span> dprt_ajax_save<span class="ot">()</span> {</span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true"></a>    check_ajax_referer<span class="ot">(</span> <span class="st">&#39;dprt_ajax_nonce&#39;</span><span class="ot">,</span> <span class="st">&#39;nonce&#39;</span> <span class="ot">);</span></span>
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true"></a>    <span class="co">// Process request</span></span>
<span id="cb8-12"><a href="#cb8-12" aria-hidden="true"></a>}</span></code></pre>
</div>
<p>Every form submission, AJAX request, and state-changing action requires nonce verification.</p>
<h2 id="user-capability-checks">User Capability Checks</h2>
<p>Verify users have permission before allowing actions:</p>
<div class="sourceCode" id="cb9">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true"></a><span class="co">// Check specific capability</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true"></a><span class="kw">if</span> <span class="ot">(</span> ! current_user_can<span class="ot">(</span> <span class="st">&#39;manage_options&#39;</span> <span class="ot">)</span> <span class="ot">)</span> {</span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true"></a>    wp_die<span class="ot">(</span> <span class="st">&#39;Unauthorized access&#39;</span> <span class="ot">);</span></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true"></a>}</span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true"></a></span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true"></a><span class="co">// Check for specific post</span></span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true"></a><span class="kw">if</span> <span class="ot">(</span> ! current_user_can<span class="ot">(</span> <span class="st">&#39;edit_post&#39;</span><span class="ot">,</span> <span class="kw">$post_id</span> <span class="ot">)</span> <span class="ot">)</span> {</span>
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true"></a>    wp_die<span class="ot">(</span> <span class="st">&#39;You cannot edit this post&#39;</span> <span class="ot">);</span></span>
<span id="cb9-9"><a href="#cb9-9" aria-hidden="true"></a>}</span>
<span id="cb9-10"><a href="#cb9-10" aria-hidden="true"></a></span>
<span id="cb9-11"><a href="#cb9-11" aria-hidden="true"></a><span class="co">// Check multiple capabilities</span></span>
<span id="cb9-12"><a href="#cb9-12" aria-hidden="true"></a><span class="kw">if</span> <span class="ot">(</span> ! current_user_can<span class="ot">(</span> <span class="st">&#39;edit_posts&#39;</span> <span class="ot">)</span> &amp;&amp; ! current_user_can<span class="ot">(</span> <span class="st">&#39;edit_pages&#39;</span> <span class="ot">)</span> <span class="ot">)</span> {</span>
<span id="cb9-13"><a href="#cb9-13" aria-hidden="true"></a>    <span class="kw">return</span><span class="ot">;</span></span>
<span id="cb9-14"><a href="#cb9-14" aria-hidden="true"></a>}</span></code></pre>
</div>
<p>Common capabilities:</p>
<ul>
<li><code>manage_options</code> &#8211; Administrators only</li>
<li><code>edit_posts</code> &#8211; Can edit posts</li>
<li><code>publish_posts</code> &#8211; Can publish posts</li>
<li><code>edit_others_posts</code> &#8211; Can edit posts by other users</li>
</ul>
<h2 id="data-sanitization">Data Sanitization</h2>
<p>Clean all user input before processing:</p>
<div class="sourceCode" id="cb10">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true"></a><span class="co">// Text fields</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true"></a><span class="kw">$text</span> = sanitize_text_field<span class="ot">(</span> <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;text_field&#39;</span><span class="ot">]</span> <span class="ot">);</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true"></a></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true"></a><span class="co">// Textareas</span></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true"></a><span class="kw">$content</span> = sanitize_textarea_field<span class="ot">(</span> <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;content&#39;</span><span class="ot">]</span> <span class="ot">);</span></span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true"></a></span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true"></a><span class="co">// Email addresses</span></span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true"></a><span class="kw">$email</span> = sanitize_email<span class="ot">(</span> <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;email&#39;</span><span class="ot">]</span> <span class="ot">);</span></span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true"></a></span>
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true"></a><span class="co">// URLs</span></span>
<span id="cb10-11"><a href="#cb10-11" aria-hidden="true"></a><span class="kw">$url</span> = esc_url_raw<span class="ot">(</span> <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;url&#39;</span><span class="ot">]</span> <span class="ot">);</span></span>
<span id="cb10-12"><a href="#cb10-12" aria-hidden="true"></a></span>
<span id="cb10-13"><a href="#cb10-13" aria-hidden="true"></a><span class="co">// File names</span></span>
<span id="cb10-14"><a href="#cb10-14" aria-hidden="true"></a><span class="kw">$filename</span> = sanitize_file_name<span class="ot">(</span> <span class="kw">$_FILES</span><span class="ot">[</span><span class="st">&#39;file&#39;</span><span class="ot">][</span><span class="st">&#39;name&#39;</span><span class="ot">]</span> <span class="ot">);</span></span>
<span id="cb10-15"><a href="#cb10-15" aria-hidden="true"></a></span>
<span id="cb10-16"><a href="#cb10-16" aria-hidden="true"></a><span class="co">// HTML class names</span></span>
<span id="cb10-17"><a href="#cb10-17" aria-hidden="true"></a><span class="kw">$class</span> = sanitize_html_class<span class="ot">(</span> <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;class&#39;</span><span class="ot">]</span> <span class="ot">);</span></span>
<span id="cb10-18"><a href="#cb10-18" aria-hidden="true"></a></span>
<span id="cb10-19"><a href="#cb10-19" aria-hidden="true"></a><span class="co">// Keys</span></span>
<span id="cb10-20"><a href="#cb10-20" aria-hidden="true"></a><span class="kw">$key</span> = sanitize_key<span class="ot">(</span> <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;key&#39;</span><span class="ot">]</span> <span class="ot">);</span></span>
<span id="cb10-21"><a href="#cb10-21" aria-hidden="true"></a></span>
<span id="cb10-22"><a href="#cb10-22" aria-hidden="true"></a><span class="co">// Integers</span></span>
<span id="cb10-23"><a href="#cb10-23" aria-hidden="true"></a><span class="kw">$number</span> = absint<span class="ot">(</span> <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;number&#39;</span><span class="ot">]</span> <span class="ot">);</span></span></code></pre>
</div>
<p>Sanitize input, escape output. This principle prevents most vulnerabilities.</p>
<h2 id="file-upload-security">File Upload Security</h2>
<p>File uploads are high-risk operations:</p>
<div class="sourceCode" id="cb11">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true"></a><span class="kw">function</span> dprt_handle_file_upload<span class="ot">()</span> {</span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true"></a>    <span class="kw">if</span> <span class="ot">(</span> ! <span class="kw">isset</span><span class="ot">(</span> <span class="kw">$_FILES</span><span class="ot">[</span><span class="st">&#39;file&#39;</span><span class="ot">]</span> <span class="ot">)</span> <span class="ot">)</span> {</span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true"></a>        <span class="kw">return</span><span class="ot">;</span></span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true"></a>    }</span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true"></a></span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true"></a>    <span class="co">// Verify nonce</span></span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true"></a>    check_admin_referer<span class="ot">(</span> <span class="st">&#39;dprt_upload&#39;</span><span class="ot">,</span> <span class="st">&#39;nonce&#39;</span> <span class="ot">);</span></span>
<span id="cb11-8"><a href="#cb11-8" aria-hidden="true"></a></span>
<span id="cb11-9"><a href="#cb11-9" aria-hidden="true"></a>    <span class="co">// Check capability</span></span>
<span id="cb11-10"><a href="#cb11-10" aria-hidden="true"></a>    <span class="kw">if</span> <span class="ot">(</span> ! current_user_can<span class="ot">(</span> <span class="st">&#39;upload_files&#39;</span> <span class="ot">)</span> <span class="ot">)</span> {</span>
<span id="cb11-11"><a href="#cb11-11" aria-hidden="true"></a>        wp_die<span class="ot">(</span> <span class="st">&#39;Insufficient permissions&#39;</span> <span class="ot">);</span></span>
<span id="cb11-12"><a href="#cb11-12" aria-hidden="true"></a>    }</span>
<span id="cb11-13"><a href="#cb11-13" aria-hidden="true"></a></span>
<span id="cb11-14"><a href="#cb11-14" aria-hidden="true"></a>    <span class="co">// Validate file type</span></span>
<span id="cb11-15"><a href="#cb11-15" aria-hidden="true"></a>    <span class="kw">$allowed_types</span> = <span class="kw">array</span><span class="ot">(</span> <span class="st">&#39;image/jpeg&#39;</span><span class="ot">,</span> <span class="st">&#39;image/png&#39;</span><span class="ot">,</span> <span class="st">&#39;image/gif&#39;</span> <span class="ot">);</span></span>
<span id="cb11-16"><a href="#cb11-16" aria-hidden="true"></a>    <span class="kw">$file_type</span> = <span class="kw">$_FILES</span><span class="ot">[</span><span class="st">&#39;file&#39;</span><span class="ot">][</span><span class="st">&#39;type&#39;</span><span class="ot">];</span></span>
<span id="cb11-17"><a href="#cb11-17" aria-hidden="true"></a></span>
<span id="cb11-18"><a href="#cb11-18" aria-hidden="true"></a>    <span class="kw">if</span> <span class="ot">(</span> ! <span class="fu">in_array</span><span class="ot">(</span> <span class="kw">$file_type</span><span class="ot">,</span> <span class="kw">$allowed_types</span> <span class="ot">)</span> <span class="ot">)</span> {</span>
<span id="cb11-19"><a href="#cb11-19" aria-hidden="true"></a>        wp_die<span class="ot">(</span> <span class="st">&#39;Invalid file type&#39;</span> <span class="ot">);</span></span>
<span id="cb11-20"><a href="#cb11-20" aria-hidden="true"></a>    }</span>
<span id="cb11-21"><a href="#cb11-21" aria-hidden="true"></a></span>
<span id="cb11-22"><a href="#cb11-22" aria-hidden="true"></a>    <span class="co">// Use WordPress upload handler</span></span>
<span id="cb11-23"><a href="#cb11-23" aria-hidden="true"></a>    <span class="kw">$upload</span> = wp_handle_upload<span class="ot">(</span></span>
<span id="cb11-24"><a href="#cb11-24" aria-hidden="true"></a>        <span class="kw">$_FILES</span><span class="ot">[</span><span class="st">&#39;file&#39;</span><span class="ot">],</span></span>
<span id="cb11-25"><a href="#cb11-25" aria-hidden="true"></a>        <span class="kw">array</span><span class="ot">(</span> <span class="st">&#39;test_form&#39;</span> =&gt; <span class="kw">false</span> <span class="ot">)</span></span>
<span id="cb11-26"><a href="#cb11-26" aria-hidden="true"></a>    <span class="ot">);</span></span>
<span id="cb11-27"><a href="#cb11-27" aria-hidden="true"></a></span>
<span id="cb11-28"><a href="#cb11-28" aria-hidden="true"></a>    <span class="kw">if</span> <span class="ot">(</span> <span class="kw">isset</span><span class="ot">(</span> <span class="kw">$upload</span><span class="ot">[</span><span class="st">&#39;error&#39;</span><span class="ot">]</span> <span class="ot">)</span> <span class="ot">)</span> {</span>
<span id="cb11-29"><a href="#cb11-29" aria-hidden="true"></a>        wp_die<span class="ot">(</span> <span class="kw">$upload</span><span class="ot">[</span><span class="st">&#39;error&#39;</span><span class="ot">]</span> <span class="ot">);</span></span>
<span id="cb11-30"><a href="#cb11-30" aria-hidden="true"></a>    }</span>
<span id="cb11-31"><a href="#cb11-31" aria-hidden="true"></a></span>
<span id="cb11-32"><a href="#cb11-32" aria-hidden="true"></a>    <span class="co">// File uploaded successfully</span></span>
<span id="cb11-33"><a href="#cb11-33" aria-hidden="true"></a>    <span class="kw">$file_url</span> = <span class="kw">$upload</span><span class="ot">[</span><span class="st">&#39;url&#39;</span><span class="ot">];</span></span>
<span id="cb11-34"><a href="#cb11-34" aria-hidden="true"></a>}</span></code></pre>
</div>
<p>Never trust uploaded files. Validate type, size, and content.</p>
<h2 id="preventing-direct-file-access">Preventing Direct File Access</h2>
<p>Prevent users from accessing plugin files directly:</p>
<div class="sourceCode" id="cb12">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true"></a><span class="kw">&lt;?php</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true"></a><span class="co">// At the top of every PHP file</span></span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true"></a><span class="kw">if</span> <span class="ot">(</span> ! <span class="fu">defined</span><span class="ot">(</span> <span class="st">&#39;ABSPATH&#39;</span> <span class="ot">)</span> <span class="ot">)</span> {</span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true"></a>    <span class="kw">exit</span><span class="ot">;</span> <span class="co">// Exit if accessed directly</span></span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true"></a>}</span></code></pre>
</div>
<p>This prevents attackers from executing PHP files outside WordPress context.</p>
<h2 id="secure-ajax-implementation">Secure AJAX Implementation</h2>
<p>AJAX requests need the same security as form submissions:</p>
<div class="sourceCode" id="cb13">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true"></a><span class="co">// Enqueue script with nonce</span></span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true"></a><span class="kw">function</span> dprt_enqueue_ajax_script<span class="ot">()</span> {</span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true"></a>    wp_enqueue_script<span class="ot">(</span> <span class="st">&#39;dprt-ajax&#39;</span><span class="ot">,</span> plugin_dir_url<span class="ot">(</span> <span class="kw">__FILE__</span> <span class="ot">)</span> . <span class="st">&#39;ajax.js&#39;</span><span class="ot">,</span> <span class="kw">array</span><span class="ot">(</span> <span class="st">&#39;jquery&#39;</span> <span class="ot">)</span> <span class="ot">);</span></span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true"></a>    wp_localize_script<span class="ot">(</span> <span class="st">&#39;dprt-ajax&#39;</span><span class="ot">,</span> <span class="st">&#39;dprtAjax&#39;</span><span class="ot">,</span> <span class="kw">array</span><span class="ot">(</span></span>
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true"></a>        <span class="st">&#39;ajaxurl&#39;</span> =&gt; admin_url<span class="ot">(</span> <span class="st">&#39;admin-ajax.php&#39;</span> <span class="ot">),</span></span>
<span id="cb13-6"><a href="#cb13-6" aria-hidden="true"></a>        <span class="st">&#39;nonce&#39;</span> =&gt; wp_create_nonce<span class="ot">(</span> <span class="st">&#39;dprt_ajax&#39;</span> <span class="ot">)</span></span>
<span id="cb13-7"><a href="#cb13-7" aria-hidden="true"></a>    <span class="ot">)</span> <span class="ot">);</span></span>
<span id="cb13-8"><a href="#cb13-8" aria-hidden="true"></a>}</span>
<span id="cb13-9"><a href="#cb13-9" aria-hidden="true"></a></span>
<span id="cb13-10"><a href="#cb13-10" aria-hidden="true"></a><span class="co">// AJAX handler</span></span>
<span id="cb13-11"><a href="#cb13-11" aria-hidden="true"></a><span class="kw">function</span> dprt_ajax_handler<span class="ot">()</span> {</span>
<span id="cb13-12"><a href="#cb13-12" aria-hidden="true"></a>    <span class="co">// Verify nonce</span></span>
<span id="cb13-13"><a href="#cb13-13" aria-hidden="true"></a>    check_ajax_referer<span class="ot">(</span> <span class="st">&#39;dprt_ajax&#39;</span><span class="ot">,</span> <span class="st">&#39;nonce&#39;</span> <span class="ot">);</span></span>
<span id="cb13-14"><a href="#cb13-14" aria-hidden="true"></a></span>
<span id="cb13-15"><a href="#cb13-15" aria-hidden="true"></a>    <span class="co">// Check capabilities</span></span>
<span id="cb13-16"><a href="#cb13-16" aria-hidden="true"></a>    <span class="kw">if</span> <span class="ot">(</span> ! current_user_can<span class="ot">(</span> <span class="st">&#39;manage_options&#39;</span> <span class="ot">)</span> <span class="ot">)</span> {</span>
<span id="cb13-17"><a href="#cb13-17" aria-hidden="true"></a>        wp_send_json_error<span class="ot">(</span> <span class="st">&#39;Insufficient permissions&#39;</span> <span class="ot">);</span></span>
<span id="cb13-18"><a href="#cb13-18" aria-hidden="true"></a>    }</span>
<span id="cb13-19"><a href="#cb13-19" aria-hidden="true"></a></span>
<span id="cb13-20"><a href="#cb13-20" aria-hidden="true"></a>    <span class="co">// Sanitize input</span></span>
<span id="cb13-21"><a href="#cb13-21" aria-hidden="true"></a>    <span class="kw">$data</span> = sanitize_text_field<span class="ot">(</span> <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;data&#39;</span><span class="ot">]</span> <span class="ot">);</span></span>
<span id="cb13-22"><a href="#cb13-22" aria-hidden="true"></a></span>
<span id="cb13-23"><a href="#cb13-23" aria-hidden="true"></a>    <span class="co">// Process and respond</span></span>
<span id="cb13-24"><a href="#cb13-24" aria-hidden="true"></a>    wp_send_json_success<span class="ot">(</span> <span class="kw">array</span><span class="ot">(</span> <span class="st">&#39;result&#39;</span> =&gt; <span class="kw">$processed_data</span> <span class="ot">)</span> <span class="ot">);</span></span>
<span id="cb13-25"><a href="#cb13-25" aria-hidden="true"></a>}</span>
<span id="cb13-26"><a href="#cb13-26" aria-hidden="true"></a>add_action<span class="ot">(</span> <span class="st">&#39;wp_ajax_dprt_action&#39;</span><span class="ot">,</span> <span class="st">&#39;dprt_ajax_handler&#39;</span> <span class="ot">);</span></span></code></pre>
</div>
<h2 id="secure-configuration">Secure Configuration</h2>
<p>Never hardcode sensitive information:</p>
<div class="sourceCode" id="cb14">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true"></a><span class="co">// BAD - hardcoded API key</span></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true"></a><span class="kw">$api_key</span> = <span class="st">&#39;sk_live_123456789&#39;</span><span class="ot">;</span></span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true"></a></span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true"></a><span class="co">// GOOD - use constants or options</span></span>
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true"></a><span class="fu">define</span><span class="ot">(</span> <span class="st">&#39;DPRT_API_KEY&#39;</span><span class="ot">,</span> <span class="st">&#39;sk_live_123456789&#39;</span> <span class="ot">);</span> <span class="co">// In wp-config.php</span></span>
<span id="cb14-6"><a href="#cb14-6" aria-hidden="true"></a><span class="kw">$api_key</span> = <span class="fu">defined</span><span class="ot">(</span> <span class="st">&#39;DPRT_API_KEY&#39;</span> <span class="ot">)</span> <span class="ot">?</span> <span class="kw">DPRT_API_KEY</span> <span class="ot">:</span> get_option<span class="ot">(</span> <span class="st">&#39;dprt_api_key&#39;</span> <span class="ot">);</span></span></code></pre>
</div>
<p>Store sensitive data in wp-config.php or use environment variables.</p>
<h2 id="security-checklist">Security Checklist</h2>
<p>Before releasing your plugin:</p>
<ul class="task-list">
<li><input type="checkbox" disabled="" /><br />
All user input sanitized</li>
<li><input type="checkbox" disabled="" /><br />
All output escaped</li>
<li><input type="checkbox" disabled="" /><br />
Nonces on all forms and AJAX requests</li>
<li><input type="checkbox" disabled="" /><br />
Capability checks on all admin functions</li>
<li><input type="checkbox" disabled="" /><br />
Prepared statements for all database queries</li>
<li><input type="checkbox" disabled="" /><br />
File upload validation</li>
<li><input type="checkbox" disabled="" /><br />
Direct file access prevention</li>
<li><input type="checkbox" disabled="" /><br />
No hardcoded credentials</li>
<li><input type="checkbox" disabled="" /><br />
Security review by another developer</li>
<li><input type="checkbox" disabled="" /><br />
Testing with security plugins</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>Security isn’t a feature—it’s a requirement. Sanitize all input, escape all output, verify nonces, check capabilities, and use prepared statements. These practices protect users and maintain WordPress ecosystem trust. Make security your default mindset, not an afterthought.</p>
<ul>
<li>Secure communication</li>
<li>Using HTTPS for API calls</li>
<li>SSL/TLS certificate validation</li>
<li>Third-party library security</li>
<li>Keeping dependencies updated</li>
<li>Vulnerability scanning tools</li>
<li>Security auditing and code review</li>
<li>Common security mistakes developers make</li>
<li>Security checklist for plugin release</li>
<li>Responsible disclosure of vulnerabilities</li>
</ul>
<p>Includes code examples, security patterns, and testing procedures for building secure, trustworthy WordPress plugins.</p>
<h2 id="external-links">External Links</h2>
<ol type="1">
<li><a href="https://developer.wordpress.org/plugins/security/">WordPress Plugin Security</a></li>
<li><a href="https://developer.wordpress.org/plugins/security/data-validation/">Data Validation Documentation</a></li>
<li><a href="https://owasp.org/www-project-top-ten/">OWASP Top 10</a></li>
<li><a href="https://wordpress.org/about/security/">WordPress Security White Paper</a></li>
<li><a href="https://wpscan.com/wordpress-security-scanner">WPScan Vulnerability Database</a></li>
</ol>
<h2 id="call-to-action">Call to Action</h2>
<p>Supercharge your development! <a href="https://acfcopilotplugin.com/">ACF Copilot Pro</a> generates ACF field groups with AI, exports to PHP, and accelerates custom field workflows—try it free!</p>
<p>The post <a href="https://developryplugins.com/wordpress-plugin-security-best-practices-prevent-common-vulnerabilities/">WordPress Plugin Security Best Practices: Prevent Common Vulnerabilities</a> appeared first on <a href="https://developryplugins.com">Developry Plugins</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>WordPress Plugin Testing: PHPUnit and Automated Testing Guide</title>
		<link>https://developryplugins.com/wordpress-plugin-testing-phpunit-and-automated-testing-guide/</link>
		
		<dc:creator><![CDATA[Krasen Slavov]]></dc:creator>
		<pubDate>Thu, 15 Jan 2026 09:00:00 +0000</pubDate>
				<category><![CDATA[WordPress Plugin Development Guide]]></category>
		<category><![CDATA[automated testing]]></category>
		<category><![CDATA[phpunit]]></category>
		<category><![CDATA[plugin testing]]></category>
		<category><![CDATA[unit tests]]></category>
		<category><![CDATA[wordpress testing]]></category>
		<guid isPermaLink="false">https://developryplugins.com/?p=174</guid>

					<description><![CDATA[<p>Automated testing ensures your WordPress plugin works reliably across updates, prevents regressions, and gives you confidence to refactor code. PHPUnit combined with the WordPress test framework provides a robust solution...</p>
<p>The post <a href="https://developryplugins.com/wordpress-plugin-testing-phpunit-and-automated-testing-guide/">WordPress Plugin Testing: PHPUnit and Automated Testing Guide</a> appeared first on <a href="https://developryplugins.com">Developry Plugins</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><!-- @format --></p>
<p>Automated testing ensures your WordPress plugin works reliably across updates, prevents regressions, and gives you confidence to refactor code. PHPUnit combined with the WordPress test framework provides a robust solution for testing plugin functionality.</p>
<h2 id="why-test-wordpress-plugins">Why Test WordPress Plugins</h2>
<p>Manual testing becomes impractical as plugins grow. Automated tests catch bugs before users do, document expected behavior, and enable safe refactoring. Tests save time in the long run by preventing regressions and reducing debugging sessions.</p>
<p>Well-tested plugins are more maintainable, easier to collaborate on, and inspire user confidence. Professional development includes comprehensive testing as a standard practice.</p>
<h2 id="understanding-test-types">Understanding Test Types</h2>
<p><strong>Unit Tests:</strong> Test individual functions or methods in isolation. Fast to run and pinpoint specific issues.</p>
<p><strong>Integration Tests:</strong> Test how components work together, including WordPress functions, database operations, and plugin interactions.</p>
<p><strong>Acceptance Tests:</strong> Test complete user workflows from end to end. Slower but verify real-world usage.</p>
<p>For WordPress plugins, you’ll primarily write integration tests since most functionality interacts with WordPress core.</p>
<h2 id="setting-up-phpunit-for-wordpress">Setting Up PHPUnit for WordPress</h2>
<p>Install PHPUnit and set up the WordPress test suite using WP-CLI:</p>
<div class="sourceCode" id="cb1">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true"></a><span class="co"># Navigate to your plugin directory</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true"></a><span class="bu">cd</span> wp-content/plugins/my-plugin</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true"></a></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true"></a><span class="co"># Generate test scaffolding</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true"></a><span class="ex">wp</span> scaffold plugin-tests my-plugin</span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true"></a></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true"></a><span class="co"># Install test suite</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true"></a><span class="fu">bash</span> bin/install-wp-tests.sh wordpress_test root <span class="st">&#39;&#39;</span> localhost latest</span></code></pre>
</div>
<p>This creates the necessary directory structure:</p>
<pre><code>my-plugin/
├── bin/
│   └── install-wp-tests.sh
├── tests/
│   ├── bootstrap.php
│   └── test-sample.php
├── phpunit.xml.dist
└── .phpcs.xml.dist</code></pre>
<h2 id="writing-your-first-test">Writing Your First Test</h2>
<p>Create a test file in the <code>tests/</code> directory:</p>
<div class="sourceCode" id="cb3">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true"></a><span class="kw">&lt;?php</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true"></a><span class="kw">class</span> Test_MyPlugin_Functions <span class="kw">extends</span> WP_UnitTestCase {</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true"></a></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true"></a>    <span class="kw">public</span> <span class="kw">function</span> test_plugin_activated<span class="ot">()</span> {</span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true"></a>        <span class="kw">$this</span>-&gt;assertTrue<span class="ot">(</span>is_plugin_active<span class="ot">(</span><span class="st">&#39;my-plugin/my-plugin.php&#39;</span><span class="ot">));</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true"></a>    }</span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true"></a></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true"></a>    <span class="kw">public</span> <span class="kw">function</span> test_custom_function_returns_expected_value<span class="ot">()</span> {</span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true"></a>        <span class="kw">$result</span> = myplugin_get_greeting<span class="ot">(</span><span class="st">&#39;John&#39;</span><span class="ot">);</span></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true"></a>        <span class="kw">$this</span>-&gt;assertEquals<span class="ot">(</span><span class="st">&#39;Hello, John!&#39;</span><span class="ot">,</span> <span class="kw">$result</span><span class="ot">);</span></span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true"></a>    }</span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true"></a></span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true"></a>    <span class="kw">public</span> <span class="kw">function</span> test_custom_post_type_registered<span class="ot">()</span> {</span>
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true"></a>        <span class="kw">$this</span>-&gt;assertTrue<span class="ot">(</span>post_type_exists<span class="ot">(</span><span class="st">&#39;myplugin_item&#39;</span><span class="ot">));</span></span>
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true"></a>    }</span>
<span id="cb3-16"><a href="#cb3-16" aria-hidden="true"></a>}</span></code></pre>
</div>
<p>Run tests from your plugin directory:</p>
<div class="sourceCode" id="cb4">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true"></a><span class="ex">phpunit</span></span></code></pre>
</div>
<h2 id="phpunit-assertions">PHPUnit Assertions</h2>
<p>Common assertions for testing WordPress plugins:</p>
<div class="sourceCode" id="cb5">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true"></a><span class="co">// Equality</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true"></a><span class="kw">$this</span>-&gt;assertEquals<span class="ot">(</span><span class="kw">$expected</span><span class="ot">,</span> <span class="kw">$actual</span><span class="ot">);</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true"></a><span class="kw">$this</span>-&gt;assertSame<span class="ot">(</span><span class="kw">$expected</span><span class="ot">,</span> <span class="kw">$actual</span><span class="ot">);</span> <span class="co">// Strict comparison</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true"></a></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true"></a><span class="co">// Boolean</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true"></a><span class="kw">$this</span>-&gt;assertTrue<span class="ot">(</span><span class="kw">$condition</span><span class="ot">);</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true"></a><span class="kw">$this</span>-&gt;assertFalse<span class="ot">(</span><span class="kw">$condition</span><span class="ot">);</span></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true"></a></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true"></a><span class="co">// Null and Empty</span></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true"></a><span class="kw">$this</span>-&gt;assertNull<span class="ot">(</span><span class="kw">$value</span><span class="ot">);</span></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true"></a><span class="kw">$this</span>-&gt;assertEmpty<span class="ot">(</span><span class="kw">$array</span><span class="ot">);</span></span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true"></a><span class="kw">$this</span>-&gt;assertNotEmpty<span class="ot">(</span><span class="kw">$array</span><span class="ot">);</span></span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true"></a></span>
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true"></a><span class="co">// Arrays and Strings</span></span>
<span id="cb5-15"><a href="#cb5-15" aria-hidden="true"></a><span class="kw">$this</span>-&gt;assertContains<span class="ot">(</span><span class="st">&#39;needle&#39;</span><span class="ot">,</span> <span class="kw">$haystack</span><span class="ot">);</span></span>
<span id="cb5-16"><a href="#cb5-16" aria-hidden="true"></a><span class="kw">$this</span>-&gt;assertCount<span class="ot">(</span><span class="dv">5</span><span class="ot">,</span> <span class="kw">$array</span><span class="ot">);</span></span>
<span id="cb5-17"><a href="#cb5-17" aria-hidden="true"></a><span class="kw">$this</span>-&gt;assertStringContainsString<span class="ot">(</span><span class="st">&#39;word&#39;</span><span class="ot">,</span> <span class="kw">$string</span><span class="ot">);</span></span>
<span id="cb5-18"><a href="#cb5-18" aria-hidden="true"></a></span>
<span id="cb5-19"><a href="#cb5-19" aria-hidden="true"></a><span class="co">// Instances and Types</span></span>
<span id="cb5-20"><a href="#cb5-20" aria-hidden="true"></a><span class="kw">$this</span>-&gt;assertInstanceOf<span class="ot">(</span>WP_Post::<span class="kw">class</span><span class="ot">,</span> <span class="kw">$post</span><span class="ot">);</span></span>
<span id="cb5-21"><a href="#cb5-21" aria-hidden="true"></a><span class="kw">$this</span>-&gt;assertIsArray<span class="ot">(</span><span class="kw">$value</span><span class="ot">);</span></span>
<span id="cb5-22"><a href="#cb5-22" aria-hidden="true"></a><span class="kw">$this</span>-&gt;assertIsString<span class="ot">(</span><span class="kw">$value</span><span class="ot">);</span></span>
<span id="cb5-23"><a href="#cb5-23" aria-hidden="true"></a></span>
<span id="cb5-24"><a href="#cb5-24" aria-hidden="true"></a><span class="co">// WordPress-specific</span></span>
<span id="cb5-25"><a href="#cb5-25" aria-hidden="true"></a><span class="kw">$this</span>-&gt;assertWPError<span class="ot">(</span><span class="kw">$result</span><span class="ot">);</span></span>
<span id="cb5-26"><a href="#cb5-26" aria-hidden="true"></a><span class="kw">$this</span>-&gt;assertNotWPError<span class="ot">(</span><span class="kw">$result</span><span class="ot">);</span></span></code></pre>
</div>
<h2 id="testing-wordpress-functions">Testing WordPress Functions</h2>
<p>Test interactions with WordPress core:</p>
<div class="sourceCode" id="cb6">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true"></a><span class="kw">class</span> Test_Post_Functions <span class="kw">extends</span> WP_UnitTestCase {</span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true"></a></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true"></a>    <span class="kw">public</span> <span class="kw">function</span> test_create_custom_post<span class="ot">()</span> {</span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true"></a>        <span class="kw">$post_id</span> = wp_insert_post<span class="ot">(</span><span class="kw">array</span><span class="ot">(</span></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true"></a>            <span class="st">&#39;post_title&#39;</span> =&gt; <span class="st">&#39;Test Post&#39;</span><span class="ot">,</span></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true"></a>            <span class="st">&#39;post_type&#39;</span> =&gt; <span class="st">&#39;myplugin_item&#39;</span><span class="ot">,</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true"></a>            <span class="st">&#39;post_status&#39;</span> =&gt; <span class="st">&#39;publish&#39;</span></span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true"></a>        <span class="ot">));</span></span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true"></a></span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true"></a>        <span class="kw">$this</span>-&gt;assertGreaterThan<span class="ot">(</span><span class="dv">0</span><span class="ot">,</span> <span class="kw">$post_id</span><span class="ot">);</span></span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true"></a>        <span class="kw">$this</span>-&gt;assertEquals<span class="ot">(</span><span class="st">&#39;myplugin_item&#39;</span><span class="ot">,</span> get_post_type<span class="ot">(</span><span class="kw">$post_id</span><span class="ot">));</span></span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true"></a>    }</span>
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true"></a></span>
<span id="cb6-14"><a href="#cb6-14" aria-hidden="true"></a>    <span class="kw">public</span> <span class="kw">function</span> test_post_meta_saved_correctly<span class="ot">()</span> {</span>
<span id="cb6-15"><a href="#cb6-15" aria-hidden="true"></a>        <span class="kw">$post_id</span> = <span class="kw">$this</span>-&gt;factory<span class="ot">()</span>-&gt;post-&gt;create<span class="ot">();</span></span>
<span id="cb6-16"><a href="#cb6-16" aria-hidden="true"></a></span>
<span id="cb6-17"><a href="#cb6-17" aria-hidden="true"></a>        add_post_meta<span class="ot">(</span><span class="kw">$post_id</span><span class="ot">,</span> <span class="st">&#39;_myplugin_rating&#39;</span><span class="ot">,</span> <span class="fl">4.5</span><span class="ot">);</span></span>
<span id="cb6-18"><a href="#cb6-18" aria-hidden="true"></a></span>
<span id="cb6-19"><a href="#cb6-19" aria-hidden="true"></a>        <span class="kw">$rating</span> = get_post_meta<span class="ot">(</span><span class="kw">$post_id</span><span class="ot">,</span> <span class="st">&#39;_myplugin_rating&#39;</span><span class="ot">,</span> <span class="kw">true</span><span class="ot">);</span></span>
<span id="cb6-20"><a href="#cb6-20" aria-hidden="true"></a>        <span class="kw">$this</span>-&gt;assertEquals<span class="ot">(</span><span class="fl">4.5</span><span class="ot">,</span> <span class="kw">$rating</span><span class="ot">);</span></span>
<span id="cb6-21"><a href="#cb6-21" aria-hidden="true"></a>    }</span>
<span id="cb6-22"><a href="#cb6-22" aria-hidden="true"></a>}</span></code></pre>
</div>
<h2 id="using-factories-for-test-data">Using Factories for Test Data</h2>
<p>WordPress test framework includes factories for generating test data:</p>
<div class="sourceCode" id="cb7">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true"></a><span class="kw">class</span> Test_With_Factories <span class="kw">extends</span> WP_UnitTestCase {</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true"></a></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true"></a>    <span class="kw">public</span> <span class="kw">function</span> test_user_can_edit_own_post<span class="ot">()</span> {</span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true"></a>        <span class="co">// Create test user</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true"></a>        <span class="kw">$user_id</span> = <span class="kw">$this</span>-&gt;factory<span class="ot">()</span>-&gt;user-&gt;create<span class="ot">(</span><span class="kw">array</span><span class="ot">(</span></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true"></a>            <span class="st">&#39;role&#39;</span> =&gt; <span class="st">&#39;author&#39;</span></span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true"></a>        <span class="ot">));</span></span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true"></a></span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true"></a>        <span class="co">// Create test post owned by user</span></span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true"></a>        <span class="kw">$post_id</span> = <span class="kw">$this</span>-&gt;factory<span class="ot">()</span>-&gt;post-&gt;create<span class="ot">(</span><span class="kw">array</span><span class="ot">(</span></span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true"></a>            <span class="st">&#39;post_author&#39;</span> =&gt; <span class="kw">$user_id</span></span>
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true"></a>        <span class="ot">));</span></span>
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true"></a></span>
<span id="cb7-14"><a href="#cb7-14" aria-hidden="true"></a>        wp_set_current_user<span class="ot">(</span><span class="kw">$user_id</span><span class="ot">);</span></span>
<span id="cb7-15"><a href="#cb7-15" aria-hidden="true"></a></span>
<span id="cb7-16"><a href="#cb7-16" aria-hidden="true"></a>        <span class="kw">$this</span>-&gt;assertTrue<span class="ot">(</span>current_user_can<span class="ot">(</span><span class="st">&#39;edit_post&#39;</span><span class="ot">,</span> <span class="kw">$post_id</span><span class="ot">));</span></span>
<span id="cb7-17"><a href="#cb7-17" aria-hidden="true"></a>    }</span>
<span id="cb7-18"><a href="#cb7-18" aria-hidden="true"></a></span>
<span id="cb7-19"><a href="#cb7-19" aria-hidden="true"></a>    <span class="kw">public</span> <span class="kw">function</span> test_category_assignment<span class="ot">()</span> {</span>
<span id="cb7-20"><a href="#cb7-20" aria-hidden="true"></a>        <span class="kw">$category_id</span> = <span class="kw">$this</span>-&gt;factory<span class="ot">()</span>-&gt;category-&gt;create<span class="ot">(</span><span class="kw">array</span><span class="ot">(</span></span>
<span id="cb7-21"><a href="#cb7-21" aria-hidden="true"></a>            <span class="st">&#39;name&#39;</span> =&gt; <span class="st">&#39;Test Category&#39;</span></span>
<span id="cb7-22"><a href="#cb7-22" aria-hidden="true"></a>        <span class="ot">));</span></span>
<span id="cb7-23"><a href="#cb7-23" aria-hidden="true"></a></span>
<span id="cb7-24"><a href="#cb7-24" aria-hidden="true"></a>        <span class="kw">$post_id</span> = <span class="kw">$this</span>-&gt;factory<span class="ot">()</span>-&gt;post-&gt;create<span class="ot">();</span></span>
<span id="cb7-25"><a href="#cb7-25" aria-hidden="true"></a></span>
<span id="cb7-26"><a href="#cb7-26" aria-hidden="true"></a>        wp_set_post_categories<span class="ot">(</span><span class="kw">$post_id</span><span class="ot">,</span> <span class="kw">array</span><span class="ot">(</span><span class="kw">$category_id</span><span class="ot">));</span></span>
<span id="cb7-27"><a href="#cb7-27" aria-hidden="true"></a></span>
<span id="cb7-28"><a href="#cb7-28" aria-hidden="true"></a>        <span class="kw">$categories</span> = wp_get_post_categories<span class="ot">(</span><span class="kw">$post_id</span><span class="ot">);</span></span>
<span id="cb7-29"><a href="#cb7-29" aria-hidden="true"></a>        <span class="kw">$this</span>-&gt;assertContains<span class="ot">(</span><span class="kw">$category_id</span><span class="ot">,</span> <span class="kw">$categories</span><span class="ot">);</span></span>
<span id="cb7-30"><a href="#cb7-30" aria-hidden="true"></a>    }</span>
<span id="cb7-31"><a href="#cb7-31" aria-hidden="true"></a>}</span></code></pre>
</div>
<p>Available factories:</p>
<ul>
<li><code>$this-&gt;factory()-&gt;post</code> &#8211; Posts</li>
<li><code>$this-&gt;factory()-&gt;user</code> &#8211; Users</li>
<li><code>$this-&gt;factory()-&gt;comment</code> &#8211; Comments</li>
<li><code>$this-&gt;factory()-&gt;term</code> &#8211; Terms</li>
<li><code>$this-&gt;factory()-&gt;category</code> &#8211; Categories</li>
<li><code>$this-&gt;factory()-&gt;tag</code> &#8211; Tags</li>
</ul>
<h2 id="setup-and-teardown-methods">setUp() and tearDown() Methods</h2>
<p>Prepare test environment before each test and clean up after:</p>
<div class="sourceCode" id="cb8">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true"></a><span class="kw">class</span> Test_With_Setup <span class="kw">extends</span> WP_UnitTestCase {</span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true"></a></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true"></a>    <span class="kw">protected</span> <span class="kw">$plugin</span><span class="ot">;</span></span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true"></a>    <span class="kw">protected</span> <span class="kw">$test_post_id</span><span class="ot">;</span></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true"></a></span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true"></a>    <span class="kw">public</span> <span class="kw">function</span> setUp<span class="ot">()</span>: <span class="kw">void</span> {</span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true"></a>        parent::setUp<span class="ot">();</span></span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true"></a></span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true"></a>        <span class="co">// Runs before each test</span></span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true"></a>        <span class="kw">$this</span>-&gt;plugin = <span class="kw">new</span> MyPlugin<span class="ot">();</span></span>
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true"></a>        <span class="kw">$this</span>-&gt;test_post_id = <span class="kw">$this</span>-&gt;factory<span class="ot">()</span>-&gt;post-&gt;create<span class="ot">();</span></span>
<span id="cb8-12"><a href="#cb8-12" aria-hidden="true"></a>    }</span>
<span id="cb8-13"><a href="#cb8-13" aria-hidden="true"></a></span>
<span id="cb8-14"><a href="#cb8-14" aria-hidden="true"></a>    <span class="kw">public</span> <span class="kw">function</span> tearDown<span class="ot">()</span>: <span class="kw">void</span> {</span>
<span id="cb8-15"><a href="#cb8-15" aria-hidden="true"></a>        <span class="co">// Runs after each test</span></span>
<span id="cb8-16"><a href="#cb8-16" aria-hidden="true"></a>        wp_delete_post<span class="ot">(</span><span class="kw">$this</span>-&gt;test_post_id<span class="ot">,</span> <span class="kw">true</span><span class="ot">);</span></span>
<span id="cb8-17"><a href="#cb8-17" aria-hidden="true"></a>        <span class="kw">unset</span><span class="ot">(</span><span class="kw">$this</span>-&gt;plugin<span class="ot">);</span></span>
<span id="cb8-18"><a href="#cb8-18" aria-hidden="true"></a></span>
<span id="cb8-19"><a href="#cb8-19" aria-hidden="true"></a>        parent::tearDown<span class="ot">();</span></span>
<span id="cb8-20"><a href="#cb8-20" aria-hidden="true"></a>    }</span>
<span id="cb8-21"><a href="#cb8-21" aria-hidden="true"></a></span>
<span id="cb8-22"><a href="#cb8-22" aria-hidden="true"></a>    <span class="kw">public</span> <span class="kw">function</span> test_plugin_initialized<span class="ot">()</span> {</span>
<span id="cb8-23"><a href="#cb8-23" aria-hidden="true"></a>        <span class="kw">$this</span>-&gt;assertInstanceOf<span class="ot">(</span>MyPlugin::<span class="kw">class</span><span class="ot">,</span> <span class="kw">$this</span>-&gt;plugin<span class="ot">);</span></span>
<span id="cb8-24"><a href="#cb8-24" aria-hidden="true"></a>    }</span>
<span id="cb8-25"><a href="#cb8-25" aria-hidden="true"></a></span>
<span id="cb8-26"><a href="#cb8-26" aria-hidden="true"></a>    <span class="kw">public</span> <span class="kw">function</span> test_post_exists<span class="ot">()</span> {</span>
<span id="cb8-27"><a href="#cb8-27" aria-hidden="true"></a>        <span class="kw">$this</span>-&gt;assertNotNull<span class="ot">(</span>get_post<span class="ot">(</span><span class="kw">$this</span>-&gt;test_post_id<span class="ot">));</span></span>
<span id="cb8-28"><a href="#cb8-28" aria-hidden="true"></a>    }</span>
<span id="cb8-29"><a href="#cb8-29" aria-hidden="true"></a>}</span></code></pre>
</div>
<h2 id="testing-hooks-and-filters">Testing Hooks and Filters</h2>
<p>Verify actions and filters execute correctly:</p>
<div class="sourceCode" id="cb9">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true"></a><span class="kw">class</span> Test_Hooks <span class="kw">extends</span> WP_UnitTestCase {</span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true"></a></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true"></a>    <span class="kw">public</span> <span class="kw">function</span> test_action_adds_meta_box<span class="ot">()</span> {</span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true"></a>        <span class="kw">global</span> <span class="kw">$wp_meta_boxes</span><span class="ot">;</span></span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true"></a></span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true"></a>        do_action<span class="ot">(</span><span class="st">&#39;add_meta_boxes&#39;</span><span class="ot">);</span></span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true"></a></span>
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true"></a>        <span class="kw">$this</span>-&gt;assertArrayHasKey<span class="ot">(</span><span class="st">&#39;myplugin_meta_box&#39;</span><span class="ot">,</span></span>
<span id="cb9-9"><a href="#cb9-9" aria-hidden="true"></a>            <span class="kw">$wp_meta_boxes</span><span class="ot">[</span><span class="st">&#39;post&#39;</span><span class="ot">][</span><span class="st">&#39;normal&#39;</span><span class="ot">][</span><span class="st">&#39;high&#39;</span><span class="ot">]);</span></span>
<span id="cb9-10"><a href="#cb9-10" aria-hidden="true"></a>    }</span>
<span id="cb9-11"><a href="#cb9-11" aria-hidden="true"></a></span>
<span id="cb9-12"><a href="#cb9-12" aria-hidden="true"></a>    <span class="kw">public</span> <span class="kw">function</span> test_filter_modifies_title<span class="ot">()</span> {</span>
<span id="cb9-13"><a href="#cb9-13" aria-hidden="true"></a>        add_filter<span class="ot">(</span><span class="st">&#39;the_title&#39;</span><span class="ot">,</span> <span class="st">&#39;myplugin_modify_title&#39;</span><span class="ot">);</span></span>
<span id="cb9-14"><a href="#cb9-14" aria-hidden="true"></a></span>
<span id="cb9-15"><a href="#cb9-15" aria-hidden="true"></a>        <span class="kw">$post_id</span> = <span class="kw">$this</span>-&gt;factory<span class="ot">()</span>-&gt;post-&gt;create<span class="ot">(</span><span class="kw">array</span><span class="ot">(</span></span>
<span id="cb9-16"><a href="#cb9-16" aria-hidden="true"></a>            <span class="st">&#39;post_title&#39;</span> =&gt; <span class="st">&#39;Original Title&#39;</span></span>
<span id="cb9-17"><a href="#cb9-17" aria-hidden="true"></a>        <span class="ot">));</span></span>
<span id="cb9-18"><a href="#cb9-18" aria-hidden="true"></a></span>
<span id="cb9-19"><a href="#cb9-19" aria-hidden="true"></a>        <span class="kw">$title</span> = apply_filters<span class="ot">(</span><span class="st">&#39;the_title&#39;</span><span class="ot">,</span> get_the_title<span class="ot">(</span><span class="kw">$post_id</span><span class="ot">));</span></span>
<span id="cb9-20"><a href="#cb9-20" aria-hidden="true"></a></span>
<span id="cb9-21"><a href="#cb9-21" aria-hidden="true"></a>        <span class="kw">$this</span>-&gt;assertStringContainsString<span class="ot">(</span><span class="st">&#39;Modified&#39;</span><span class="ot">,</span> <span class="kw">$title</span><span class="ot">);</span></span>
<span id="cb9-22"><a href="#cb9-22" aria-hidden="true"></a>    }</span>
<span id="cb9-23"><a href="#cb9-23" aria-hidden="true"></a></span>
<span id="cb9-24"><a href="#cb9-24" aria-hidden="true"></a>    <span class="kw">public</span> <span class="kw">function</span> test_custom_filter_value<span class="ot">()</span> {</span>
<span id="cb9-25"><a href="#cb9-25" aria-hidden="true"></a>        <span class="kw">$value</span> = apply_filters<span class="ot">(</span><span class="st">&#39;myplugin_custom_filter&#39;</span><span class="ot">,</span> <span class="dv">10</span><span class="ot">);</span></span>
<span id="cb9-26"><a href="#cb9-26" aria-hidden="true"></a></span>
<span id="cb9-27"><a href="#cb9-27" aria-hidden="true"></a>        <span class="co">// Default value</span></span>
<span id="cb9-28"><a href="#cb9-28" aria-hidden="true"></a>        <span class="kw">$this</span>-&gt;assertEquals<span class="ot">(</span><span class="dv">10</span><span class="ot">,</span> <span class="kw">$value</span><span class="ot">);</span></span>
<span id="cb9-29"><a href="#cb9-29" aria-hidden="true"></a></span>
<span id="cb9-30"><a href="#cb9-30" aria-hidden="true"></a>        <span class="co">// Add filter</span></span>
<span id="cb9-31"><a href="#cb9-31" aria-hidden="true"></a>        add_filter<span class="ot">(</span><span class="st">&#39;myplugin_custom_filter&#39;</span><span class="ot">,</span> <span class="kw">function</span><span class="ot">(</span><span class="kw">$val</span><span class="ot">)</span> {</span>
<span id="cb9-32"><a href="#cb9-32" aria-hidden="true"></a>            <span class="kw">return</span> <span class="kw">$val</span> * <span class="dv">2</span><span class="ot">;</span></span>
<span id="cb9-33"><a href="#cb9-33" aria-hidden="true"></a>        }<span class="ot">);</span></span>
<span id="cb9-34"><a href="#cb9-34" aria-hidden="true"></a></span>
<span id="cb9-35"><a href="#cb9-35" aria-hidden="true"></a>        <span class="kw">$filtered_value</span> = apply_filters<span class="ot">(</span><span class="st">&#39;myplugin_custom_filter&#39;</span><span class="ot">,</span> <span class="dv">10</span><span class="ot">);</span></span>
<span id="cb9-36"><a href="#cb9-36" aria-hidden="true"></a>        <span class="kw">$this</span>-&gt;assertEquals<span class="ot">(</span><span class="dv">20</span><span class="ot">,</span> <span class="kw">$filtered_value</span><span class="ot">);</span></span>
<span id="cb9-37"><a href="#cb9-37" aria-hidden="true"></a>    }</span>
<span id="cb9-38"><a href="#cb9-38" aria-hidden="true"></a>}</span></code></pre>
</div>
<h2 id="testing-ajax-functionality">Testing AJAX Functionality</h2>
<p>Test AJAX handlers using WordPress test helpers:</p>
<div class="sourceCode" id="cb10">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true"></a><span class="kw">class</span> Test_AJAX <span class="kw">extends</span> WP_Ajax_UnitTestCase {</span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true"></a></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true"></a>    <span class="kw">public</span> <span class="kw">function</span> test_ajax_like_post<span class="ot">()</span> {</span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true"></a>        <span class="kw">$post_id</span> = <span class="kw">$this</span>-&gt;factory<span class="ot">()</span>-&gt;post-&gt;create<span class="ot">();</span></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true"></a></span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true"></a>        <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;action&#39;</span><span class="ot">]</span> = <span class="st">&#39;myplugin_like_post&#39;</span><span class="ot">;</span></span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true"></a>        <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;post_id&#39;</span><span class="ot">]</span> = <span class="kw">$post_id</span><span class="ot">;</span></span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true"></a>        <span class="kw">$_POST</span><span class="ot">[</span><span class="st">&#39;nonce&#39;</span><span class="ot">]</span> = wp_create_nonce<span class="ot">(</span><span class="st">&#39;myplugin_like_nonce&#39;</span><span class="ot">);</span></span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true"></a></span>
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true"></a>        <span class="kw">try</span> {</span>
<span id="cb10-11"><a href="#cb10-11" aria-hidden="true"></a>            <span class="kw">$this</span>-&gt;_handleAjax<span class="ot">(</span><span class="st">&#39;myplugin_like_post&#39;</span><span class="ot">);</span></span>
<span id="cb10-12"><a href="#cb10-12" aria-hidden="true"></a>        } <span class="kw">catch</span> <span class="ot">(</span>WPAjaxDieContinueException <span class="kw">$e</span><span class="ot">)</span> {</span>
<span id="cb10-13"><a href="#cb10-13" aria-hidden="true"></a>            <span class="co">// Expected exception</span></span>
<span id="cb10-14"><a href="#cb10-14" aria-hidden="true"></a>        }</span>
<span id="cb10-15"><a href="#cb10-15" aria-hidden="true"></a></span>
<span id="cb10-16"><a href="#cb10-16" aria-hidden="true"></a>        <span class="kw">$response</span> = <span class="fu">json_decode</span><span class="ot">(</span><span class="kw">$this</span>-&gt;_last_response<span class="ot">);</span></span>
<span id="cb10-17"><a href="#cb10-17" aria-hidden="true"></a></span>
<span id="cb10-18"><a href="#cb10-18" aria-hidden="true"></a>        <span class="kw">$this</span>-&gt;assertTrue<span class="ot">(</span><span class="kw">$response</span>-&gt;success<span class="ot">);</span></span>
<span id="cb10-19"><a href="#cb10-19" aria-hidden="true"></a>        <span class="kw">$this</span>-&gt;assertEquals<span class="ot">(</span><span class="dv">1</span><span class="ot">,</span> <span class="kw">$response</span>-&gt;data-&gt;likes<span class="ot">);</span></span>
<span id="cb10-20"><a href="#cb10-20" aria-hidden="true"></a>    }</span>
<span id="cb10-21"><a href="#cb10-21" aria-hidden="true"></a>}</span></code></pre>
</div>
<h2 id="testing-database-operations">Testing Database Operations</h2>
<p>Test custom database tables and queries:</p>
<div class="sourceCode" id="cb11">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true"></a><span class="kw">class</span> Test_Database <span class="kw">extends</span> WP_UnitTestCase {</span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true"></a></span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true"></a>    <span class="kw">public</span> <span class="kw">function</span> test_custom_table_created<span class="ot">()</span> {</span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true"></a>        <span class="kw">global</span> <span class="kw">$wpdb</span><span class="ot">;</span></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true"></a></span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true"></a>        myplugin_create_custom_table<span class="ot">();</span></span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true"></a></span>
<span id="cb11-8"><a href="#cb11-8" aria-hidden="true"></a>        <span class="kw">$table_name</span> = <span class="kw">$wpdb</span>-&gt;prefix . <span class="st">&#39;myplugin_data&#39;</span><span class="ot">;</span></span>
<span id="cb11-9"><a href="#cb11-9" aria-hidden="true"></a>        <span class="kw">$this</span>-&gt;assertEquals<span class="ot">(</span><span class="kw">$table_name</span><span class="ot">,</span></span>
<span id="cb11-10"><a href="#cb11-10" aria-hidden="true"></a>            <span class="kw">$wpdb</span>-&gt;get_var<span class="ot">(</span><span class="st">&quot;SHOW TABLES LIKE &#39;</span><span class="kw">$table_name</span><span class="st">&#39;&quot;</span><span class="ot">));</span></span>
<span id="cb11-11"><a href="#cb11-11" aria-hidden="true"></a>    }</span>
<span id="cb11-12"><a href="#cb11-12" aria-hidden="true"></a></span>
<span id="cb11-13"><a href="#cb11-13" aria-hidden="true"></a>    <span class="kw">public</span> <span class="kw">function</span> test_insert_custom_data<span class="ot">()</span> {</span>
<span id="cb11-14"><a href="#cb11-14" aria-hidden="true"></a>        <span class="kw">global</span> <span class="kw">$wpdb</span><span class="ot">;</span></span>
<span id="cb11-15"><a href="#cb11-15" aria-hidden="true"></a></span>
<span id="cb11-16"><a href="#cb11-16" aria-hidden="true"></a>        <span class="kw">$table_name</span> = <span class="kw">$wpdb</span>-&gt;prefix . <span class="st">&#39;myplugin_data&#39;</span><span class="ot">;</span></span>
<span id="cb11-17"><a href="#cb11-17" aria-hidden="true"></a></span>
<span id="cb11-18"><a href="#cb11-18" aria-hidden="true"></a>        <span class="kw">$result</span> = <span class="kw">$wpdb</span>-&gt;insert<span class="ot">(</span><span class="kw">$table_name</span><span class="ot">,</span> <span class="kw">array</span><span class="ot">(</span></span>
<span id="cb11-19"><a href="#cb11-19" aria-hidden="true"></a>            <span class="st">&#39;name&#39;</span> =&gt; <span class="st">&#39;Test Item&#39;</span><span class="ot">,</span></span>
<span id="cb11-20"><a href="#cb11-20" aria-hidden="true"></a>            <span class="st">&#39;value&#39;</span> =&gt; <span class="dv">100</span></span>
<span id="cb11-21"><a href="#cb11-21" aria-hidden="true"></a>        <span class="ot">));</span></span>
<span id="cb11-22"><a href="#cb11-22" aria-hidden="true"></a></span>
<span id="cb11-23"><a href="#cb11-23" aria-hidden="true"></a>        <span class="kw">$this</span>-&gt;assertNotFalse<span class="ot">(</span><span class="kw">$result</span><span class="ot">);</span></span>
<span id="cb11-24"><a href="#cb11-24" aria-hidden="true"></a>        <span class="kw">$this</span>-&gt;assertEquals<span class="ot">(</span><span class="dv">1</span><span class="ot">,</span> <span class="kw">$wpdb</span>-&gt;insert_id<span class="ot">);</span></span>
<span id="cb11-25"><a href="#cb11-25" aria-hidden="true"></a>    }</span>
<span id="cb11-26"><a href="#cb11-26" aria-hidden="true"></a>}</span></code></pre>
</div>
<h2 id="mocking-external-api-calls">Mocking External API Calls</h2>
<p>Use pre_http_request filter to mock external APIs:</p>
<div class="sourceCode" id="cb12">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true"></a><span class="kw">class</span> Test_External_API <span class="kw">extends</span> WP_UnitTestCase {</span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true"></a></span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true"></a>    <span class="kw">public</span> <span class="kw">function</span> test_api_call_returns_data<span class="ot">()</span> {</span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true"></a>        <span class="co">// Mock the HTTP request</span></span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true"></a>        add_filter<span class="ot">(</span><span class="st">&#39;pre_http_request&#39;</span><span class="ot">,</span> <span class="kw">function</span><span class="ot">(</span><span class="kw">$response</span><span class="ot">,</span> <span class="kw">$args</span><span class="ot">,</span> <span class="kw">$url</span><span class="ot">)</span> {</span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true"></a>            <span class="kw">if</span> <span class="ot">(</span><span class="fu">strpos</span><span class="ot">(</span><span class="kw">$url</span><span class="ot">,</span> <span class="st">&#39;api.example.com&#39;</span><span class="ot">)</span> !== <span class="kw">false</span><span class="ot">)</span> {</span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true"></a>                <span class="kw">return</span> <span class="kw">array</span><span class="ot">(</span></span>
<span id="cb12-8"><a href="#cb12-8" aria-hidden="true"></a>                    <span class="st">&#39;response&#39;</span> =&gt; <span class="kw">array</span><span class="ot">(</span><span class="st">&#39;code&#39;</span> =&gt; <span class="dv">200</span><span class="ot">),</span></span>
<span id="cb12-9"><a href="#cb12-9" aria-hidden="true"></a>                    <span class="st">&#39;body&#39;</span> =&gt; <span class="fu">json_encode</span><span class="ot">(</span><span class="kw">array</span><span class="ot">(</span></span>
<span id="cb12-10"><a href="#cb12-10" aria-hidden="true"></a>                        <span class="st">&#39;status&#39;</span> =&gt; <span class="st">&#39;success&#39;</span><span class="ot">,</span></span>
<span id="cb12-11"><a href="#cb12-11" aria-hidden="true"></a>                        <span class="st">&#39;data&#39;</span> =&gt; <span class="kw">array</span><span class="ot">(</span><span class="st">&#39;value&#39;</span> =&gt; <span class="dv">42</span><span class="ot">)</span></span>
<span id="cb12-12"><a href="#cb12-12" aria-hidden="true"></a>                    <span class="ot">))</span></span>
<span id="cb12-13"><a href="#cb12-13" aria-hidden="true"></a>                <span class="ot">);</span></span>
<span id="cb12-14"><a href="#cb12-14" aria-hidden="true"></a>            }</span>
<span id="cb12-15"><a href="#cb12-15" aria-hidden="true"></a>            <span class="kw">return</span> <span class="kw">$response</span><span class="ot">;</span></span>
<span id="cb12-16"><a href="#cb12-16" aria-hidden="true"></a>        }<span class="ot">,</span> <span class="dv">10</span><span class="ot">,</span> <span class="dv">3</span><span class="ot">);</span></span>
<span id="cb12-17"><a href="#cb12-17" aria-hidden="true"></a></span>
<span id="cb12-18"><a href="#cb12-18" aria-hidden="true"></a>        <span class="kw">$result</span> = myplugin_fetch_external_data<span class="ot">();</span></span>
<span id="cb12-19"><a href="#cb12-19" aria-hidden="true"></a></span>
<span id="cb12-20"><a href="#cb12-20" aria-hidden="true"></a>        <span class="kw">$this</span>-&gt;assertEquals<span class="ot">(</span><span class="dv">42</span><span class="ot">,</span> <span class="kw">$result</span><span class="ot">[</span><span class="st">&#39;value&#39;</span><span class="ot">]);</span></span>
<span id="cb12-21"><a href="#cb12-21" aria-hidden="true"></a>    }</span>
<span id="cb12-22"><a href="#cb12-22" aria-hidden="true"></a>}</span></code></pre>
</div>
<h2 id="code-coverage-analysis">Code Coverage Analysis</h2>
<p>Generate code coverage reports to identify untested code:</p>
<div class="sourceCode" id="cb13">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true"></a><span class="ex">phpunit</span> --coverage-html coverage/</span></code></pre>
</div>
<p>View the HTML report in <code>coverage/index.html</code>. Aim for at least 70% coverage for critical code paths.</p>
<p>Configure coverage in <code>phpunit.xml</code>:</p>
<div class="sourceCode" id="cb14">
<pre class="sourceCode xml"><code class="sourceCode xml"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true"></a><span class="kw">&lt;coverage</span><span class="ot"> processUncoveredFiles=</span><span class="st">&quot;true&quot;</span><span class="kw">&gt;</span></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true"></a>    <span class="kw">&lt;include&gt;</span></span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true"></a>        <span class="kw">&lt;directory</span><span class="ot"> suffix=</span><span class="st">&quot;.php&quot;</span><span class="kw">&gt;</span>./src<span class="kw">&lt;/directory&gt;</span></span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true"></a>    <span class="kw">&lt;/include&gt;</span></span>
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true"></a>    <span class="kw">&lt;exclude&gt;</span></span>
<span id="cb14-6"><a href="#cb14-6" aria-hidden="true"></a>        <span class="kw">&lt;directory&gt;</span>./vendor<span class="kw">&lt;/directory&gt;</span></span>
<span id="cb14-7"><a href="#cb14-7" aria-hidden="true"></a>        <span class="kw">&lt;directory&gt;</span>./tests<span class="kw">&lt;/directory&gt;</span></span>
<span id="cb14-8"><a href="#cb14-8" aria-hidden="true"></a>    <span class="kw">&lt;/exclude&gt;</span></span>
<span id="cb14-9"><a href="#cb14-9" aria-hidden="true"></a><span class="kw">&lt;/coverage&gt;</span></span></code></pre>
</div>
<h2 id="continuous-integration-with-github-actions">Continuous Integration with GitHub Actions</h2>
<p>Automate testing on every commit:</p>
<div class="sourceCode" id="cb15">
<pre class="sourceCode yaml"><code class="sourceCode yaml"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true"></a><span class="co"># .github/workflows/test.yml</span></span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true"></a><span class="fu">name</span><span class="kw">:</span><span class="at"> PHPUnit Tests</span></span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true"></a></span>
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true"></a><span class="fu">on</span><span class="kw">:</span><span class="at"> </span><span class="kw">[</span><span class="at">push</span><span class="kw">,</span><span class="at"> pull_request</span><span class="kw">]</span></span>
<span id="cb15-5"><a href="#cb15-5" aria-hidden="true"></a></span>
<span id="cb15-6"><a href="#cb15-6" aria-hidden="true"></a><span class="fu">jobs</span><span class="kw">:</span></span>
<span id="cb15-7"><a href="#cb15-7" aria-hidden="true"></a><span class="at">  </span><span class="fu">test</span><span class="kw">:</span></span>
<span id="cb15-8"><a href="#cb15-8" aria-hidden="true"></a><span class="at">    </span><span class="fu">runs-on</span><span class="kw">:</span><span class="at"> ubuntu-latest</span></span>
<span id="cb15-9"><a href="#cb15-9" aria-hidden="true"></a></span>
<span id="cb15-10"><a href="#cb15-10" aria-hidden="true"></a><span class="at">    </span><span class="fu">strategy</span><span class="kw">:</span></span>
<span id="cb15-11"><a href="#cb15-11" aria-hidden="true"></a><span class="at">      </span><span class="fu">matrix</span><span class="kw">:</span></span>
<span id="cb15-12"><a href="#cb15-12" aria-hidden="true"></a><span class="at">        </span><span class="fu">php</span><span class="kw">:</span><span class="at"> </span><span class="kw">[</span><span class="st">&quot;7.4&quot;</span><span class="kw">,</span><span class="at"> </span><span class="st">&quot;8.0&quot;</span><span class="kw">,</span><span class="at"> </span><span class="st">&quot;8.1&quot;</span><span class="kw">]</span></span>
<span id="cb15-13"><a href="#cb15-13" aria-hidden="true"></a><span class="at">        </span><span class="fu">wordpress</span><span class="kw">:</span><span class="at"> </span><span class="kw">[</span><span class="st">&quot;latest&quot;</span><span class="kw">,</span><span class="at"> </span><span class="st">&quot;6.0&quot;</span><span class="kw">]</span></span>
<span id="cb15-14"><a href="#cb15-14" aria-hidden="true"></a></span>
<span id="cb15-15"><a href="#cb15-15" aria-hidden="true"></a><span class="at">    </span><span class="fu">steps</span><span class="kw">:</span></span>
<span id="cb15-16"><a href="#cb15-16" aria-hidden="true"></a><span class="at">      </span><span class="kw">-</span><span class="at"> </span><span class="fu">uses</span><span class="kw">:</span><span class="at"> actions/checkout@v2</span></span>
<span id="cb15-17"><a href="#cb15-17" aria-hidden="true"></a></span>
<span id="cb15-18"><a href="#cb15-18" aria-hidden="true"></a><span class="at">      </span><span class="kw">-</span><span class="at"> </span><span class="fu">name</span><span class="kw">:</span><span class="at"> Setup PHP</span></span>
<span id="cb15-19"><a href="#cb15-19" aria-hidden="true"></a><span class="at">        </span><span class="fu">uses</span><span class="kw">:</span><span class="at"> shivammathur/setup-php@v2</span></span>
<span id="cb15-20"><a href="#cb15-20" aria-hidden="true"></a><span class="at">        </span><span class="fu">with</span><span class="kw">:</span></span>
<span id="cb15-21"><a href="#cb15-21" aria-hidden="true"></a><span class="at">          </span><span class="fu">php-version</span><span class="kw">:</span><span class="at"> ${{ matrix.php }}</span></span>
<span id="cb15-22"><a href="#cb15-22" aria-hidden="true"></a></span>
<span id="cb15-23"><a href="#cb15-23" aria-hidden="true"></a><span class="at">      </span><span class="kw">-</span><span class="at"> </span><span class="fu">name</span><span class="kw">:</span><span class="at"> Install dependencies</span></span>
<span id="cb15-24"><a href="#cb15-24" aria-hidden="true"></a><span class="at">        </span><span class="fu">run</span><span class="kw">:</span><span class="at"> composer install</span></span>
<span id="cb15-25"><a href="#cb15-25" aria-hidden="true"></a></span>
<span id="cb15-26"><a href="#cb15-26" aria-hidden="true"></a><span class="at">      </span><span class="kw">-</span><span class="at"> </span><span class="fu">name</span><span class="kw">:</span><span class="at"> Install WordPress Test Suite</span></span>
<span id="cb15-27"><a href="#cb15-27" aria-hidden="true"></a><span class="at">        </span><span class="fu">run</span><span class="kw">:</span><span class="at"> bash bin/install-wp-tests.sh wordpress_test root root localhost ${{ matrix.wordpress }}</span></span>
<span id="cb15-28"><a href="#cb15-28" aria-hidden="true"></a></span>
<span id="cb15-29"><a href="#cb15-29" aria-hidden="true"></a><span class="at">      </span><span class="kw">-</span><span class="at"> </span><span class="fu">name</span><span class="kw">:</span><span class="at"> Run PHPUnit</span></span>
<span id="cb15-30"><a href="#cb15-30" aria-hidden="true"></a><span class="at">        </span><span class="fu">run</span><span class="kw">:</span><span class="at"> phpunit</span></span></code></pre>
</div>
<h2 id="test-driven-development-tdd">Test-Driven Development (TDD)</h2>
<p>Follow the Red-Green-Refactor cycle:</p>
<ol type="1">
<li><strong>Red:</strong> Write a failing test for new functionality</li>
<li><strong>Green:</strong> Write minimal code to make the test pass</li>
<li><strong>Refactor:</strong> Improve code while keeping tests green</li>
</ol>
<p>Example TDD workflow:</p>
<div class="sourceCode" id="cb16">
<pre class="sourceCode php"><code class="sourceCode php"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true"></a><span class="co">// Step 1: Write failing test</span></span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true"></a><span class="kw">public</span> <span class="kw">function</span> test_calculate_discount<span class="ot">()</span> {</span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true"></a>    <span class="kw">$result</span> = myplugin_calculate_discount<span class="ot">(</span><span class="dv">100</span><span class="ot">,</span> <span class="dv">10</span><span class="ot">);</span></span>
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true"></a>    <span class="kw">$this</span>-&gt;assertEquals<span class="ot">(</span><span class="dv">90</span><span class="ot">,</span> <span class="kw">$result</span><span class="ot">);</span></span>
<span id="cb16-5"><a href="#cb16-5" aria-hidden="true"></a>}</span>
<span id="cb16-6"><a href="#cb16-6" aria-hidden="true"></a></span>
<span id="cb16-7"><a href="#cb16-7" aria-hidden="true"></a><span class="co">// Step 2: Implement function</span></span>
<span id="cb16-8"><a href="#cb16-8" aria-hidden="true"></a><span class="kw">function</span> myplugin_calculate_discount<span class="ot">(</span><span class="kw">$price</span><span class="ot">,</span> <span class="kw">$discount</span><span class="ot">)</span> {</span>
<span id="cb16-9"><a href="#cb16-9" aria-hidden="true"></a>    <span class="kw">return</span> <span class="kw">$price</span> - <span class="kw">$discount</span><span class="ot">;</span></span>
<span id="cb16-10"><a href="#cb16-10" aria-hidden="true"></a>}</span>
<span id="cb16-11"><a href="#cb16-11" aria-hidden="true"></a></span>
<span id="cb16-12"><a href="#cb16-12" aria-hidden="true"></a><span class="co">// Step 3: Refactor</span></span>
<span id="cb16-13"><a href="#cb16-13" aria-hidden="true"></a><span class="kw">function</span> myplugin_calculate_discount<span class="ot">(</span><span class="kw">$price</span><span class="ot">,</span> <span class="kw">$discount_percent</span><span class="ot">)</span> {</span>
<span id="cb16-14"><a href="#cb16-14" aria-hidden="true"></a>    <span class="kw">return</span> <span class="kw">$price</span> * <span class="ot">(</span><span class="dv">1</span> - <span class="kw">$discount_percent</span> / <span class="dv">100</span><span class="ot">);</span></span>
<span id="cb16-15"><a href="#cb16-15" aria-hidden="true"></a>}</span></code></pre>
</div>
<h2 id="best-practices">Best Practices</h2>
<p><strong>Test Organization:</strong> Group related tests in classes. Use descriptive test method names that explain what’s being tested.</p>
<p><strong>Independence:</strong> Each test should run independently. Don’t rely on test execution order or shared state.</p>
<p><strong>Speed:</strong> Keep tests fast. Use transactions to roll back database changes automatically. Mock external dependencies.</p>
<p><strong>Clarity:</strong> Write clear assertions with helpful failure messages. One logical assertion per test method.</p>
<p><strong>Maintenance:</strong> Update tests alongside code changes. Treat test code with the same care as production code.</p>
<p>Automated testing transforms plugin development from reactive debugging to proactive quality assurance. Invest time in testing now to save exponentially more time later.</p>
<ul>
<li>Why testing WordPress plugins matters</li>
<li>Types of tests: unit, integration, acceptance</li>
<li>Understanding PHPUnit testing framework</li>
<li>Installing PHPUnit for WordPress</li>
<li>WordPress core test suite setup</li>
<li>Creating test scaffolding with WP-CLI</li>
<li>wp scaffold plugin-tests command</li>
<li>Test directory structure</li>
<li>bootstrap.php configuration</li>
<li>Writing your first PHPUnit test</li>
<li>Test case classes extending WP_UnitTestCase</li>
<li>Test method naming conventions</li>
<li>Assertions in PHPUnit</li>
<li>assertEquals(), assertTrue(), assertFalse()</li>
<li>assertCount(), assertEmpty(), assertContains()</li>
<li>Testing WordPress functions and hooks</li>
<li>Testing actions and filters</li>
<li>Mock objects and method stubs</li>
<li>WordPress factory for test data</li>
<li>Creating test posts, users, and terms</li>
<li>setUp() and tearDown() methods</li>
<li>Database transactions in tests</li>
<li>Test fixtures and sample data</li>
<li>Testing AJAX functionality</li>
<li>Testing custom post types</li>
<li>Testing meta boxes and custom fields</li>
<li>Testing shortcodes</li>
<li>Testing widgets</li>
<li>Testing REST API endpoints</li>
<li>Testing database queries</li>
<li>Testing caching logic</li>
<li>Code coverage analysis</li>
<li>Generating coverage reports</li>
<li>Improving test coverage</li>
<li>Test-driven development (TDD) workflow</li>
<li>Writing tests before code</li>
<li>Red-Green-Refactor cycle</li>
<li>Continuous integration setup</li>
<li>GitHub Actions for WordPress testing</li>
<li>Travis CI configuration</li>
<li>GitLab CI/CD pipelines</li>
<li>Automated testing on pull requests</li>
<li>Testing multiple PHP versions</li>
<li>Testing multiple WordPress versions</li>
<li>Integration testing best practices</li>
<li>Mocking external API calls</li>
<li>Performance testing</li>
<li>Debugging failing tests</li>
<li>Common testing pitfalls</li>
<li>Real-world testing examples</li>
</ul>
<p>Includes complete test examples, CI/CD configurations, and best practices for building reliable, well-tested WordPress plugins with comprehensive test coverage.</p>
<h2 id="external-links">External Links</h2>
<ol type="1">
<li><a href="https://phpunit.de/documentation.html">PHPUnit Documentation</a></li>
<li><a href="https://make.wordpress.org/core/handbook/testing/automated-testing/phpunit/">WordPress PHPUnit Test Suite</a></li>
<li><a href="https://developer.wordpress.org/cli/commands/scaffold/plugin-tests/">WP-CLI Testing Commands</a></li>
<li><a href="https://github.com/marketplace?type=actions&amp;query=wordpress">GitHub Actions for WordPress</a></li>
<li><a href="https://develop.svn.wordpress.org/trunk/tests/phpunit/includes/">WordPress Test Library</a></li>
</ol>
<h2 id="call-to-action">Call to Action</h2>
<p>Supercharge your development! <a href="https://acfcopilotplugin.com/">ACF Copilot Pro</a> generates ACF field groups with AI, exports to PHP, and accelerates custom field workflows—try it free!</p>
<p>The post <a href="https://developryplugins.com/wordpress-plugin-testing-phpunit-and-automated-testing-guide/">WordPress Plugin Testing: PHPUnit and Automated Testing Guide</a> appeared first on <a href="https://developryplugins.com">Developry Plugins</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
