Your own plugin for customizations
A plugin is a folder with a PHP file that WordPress includes on every page load. Shop customizations are kept in a plugin of your own: it does not depend on the theme, survives WooCommerce updates and is switched off with one click.
Creating it
There is no generator command — the folder and the file are created by hand. Create the wp-content/plugins/acme-shop folder and the acme-shop.php file in it:
<?php
// wp-content/plugins/acme-shop/acme-shop.php
/**
* Plugin Name: Acme Shop
* Description: Shop customizations.
* Version: 1.0.0
* Requires Plugins: woocommerce
*/
defined('ABSPATH') || exit;The comment with Plugin Name is the header: WordPress recognizes the plugin by it. The Requires Plugins line prevents activating it without WooCommerce. The ABSPATH check forbids opening the file directly by its address. The plugin appears in Plugins → Installed Plugins — press Activate.
Checking for WooCommerce
Your code is loaded only when WooCommerce is loaded. Add to the end of acme-shop.php:
// wp-content/plugins/acme-shop/acme-shop.php
add_action('plugins_loaded', function () {
if (!class_exists('WooCommerce')) {
return;
}
require __DIR__ . '/includes/storefront.php';
});The plugins_loaded hook fires when all plugins are included. If WooCommerce is switched off, the function returns and the site does not crash with a class not found error.
The first code file
Create includes/storefront.php:
// wp-content/plugins/acme-shop/includes/storefront.php
add_action('woocommerce_before_shop_loop', function () {
echo '<p class="acme-note">Free shipping from 50 €</p>';
});Open /shop/: the line is now above the product list. Like any PHP file, this one starts with <?php — in the course examples that first line is omitted and the file path is shown instead.
File layout
During the course the plugin grows into this structure:
acme-shop/
├── acme-shop.php main file: header and includes
├── includes/
│ ├── storefront.php storefront hooks
│ ├── products.php working with products
│ ├── cart.php the cart
│ ├── checkout.php checkout
│ ├── orders.php orders
│ └── admin.php admin fields and settings
├── templates/ your own templates
└── assets/ styles and scriptsEvery new file in includes is included with a require line in the main file, next to storefront.php. Later lessons take this for granted.
HPOS compatibility
The plugin must state that it can work with the new order storage. Add to acme-shop.php:
// wp-content/plugins/acme-shop/acme-shop.php
add_action('before_woocommerce_init', function () {
if (class_exists(\Automattic\WooCommerce\Utilities\FeaturesUtil::class)) {
\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility(
'custom_order_tables',
__FILE__,
true
);
}
});Without this declaration WooCommerce shows an incompatible plugin warning in the admin panel. The call must be in the main file: __FILE__ tells which plugin makes the statement.
Where code must not go
| Place | Why not |
|---|---|
wp-content/plugins/woocommerce/ | An update wipes the edits |
The theme's functions.php | The logic is lost when the theme changes |
| Snippet plugins in the admin panel | The code does not get into version control |
Only appearance stays in the theme: styles and template copies.
Common mistake
WooCommerce functions are called right in the body of the main file: $product = wc_get_product(12); at the top level. At that moment WooCommerce may not be loaded yet — the site crashes. All code must run inside functions attached to hooks.