SponsoredHosinger Special Offer

WP+ WordPress Plugin Development

Build WordPress plugins from scratch: actions and filters, shortcodes, settings pages, custom post types, meta boxes, AJAX, REST endpoints, custom tables, cron and blocks.

Plugin Structure, Paths and Prefixes

WordPress Plugin Development Lesson 3 of 27 ~3 min read

Overview

A plugin that grows beyond a few functions needs a clear folder structure, a set of path and URL constants built from WordPress helpers, and a unique prefix on every name so it can never collide with another plugin.

A real plugin's folders

gdc-books/
├── gdc-books.php          Main file: header, constants, loader
├── uninstall.php          Runs when the plugin is deleted
├── readme.txt             For WordPress.org
├── includes/              PHP that runs everywhere
│   ├── post-types.php
│   ├── shortcodes.php
│   └── rest-api.php
├── admin/                 PHP that only runs in the dashboard
│   ├── settings-page.php
│   └── meta-boxes.php
├── public/                Front-end-only PHP
├── assets/
│   ├── css/
│   └── js/
├── templates/             HTML a theme may override
├── blocks/                Block source code
└── languages/

Path and URL constants

Define these once in the main file. Every other file uses them instead of guessing at paths:

<?php
define( 'GDC_BOOKS_VERSION', '1.0.0' );
define( 'GDC_BOOKS_FILE', __FILE__ );
define( 'GDC_BOOKS_PATH', plugin_dir_path( __FILE__ ) );   // /var/www/.../plugins/gdc-books/
define( 'GDC_BOOKS_URL', plugin_dir_url( __FILE__ ) );     // https://site.com/wp-content/plugins/gdc-books/
define( 'GDC_BOOKS_BASENAME', plugin_basename( __FILE__ ) ); // gdc-books/gdc-books.php

require_once GDC_BOOKS_PATH . 'includes/post-types.php';
require_once GDC_BOOKS_PATH . 'includes/shortcodes.php';

if ( is_admin() ) {
    require_once GDC_BOOKS_PATH . 'admin/settings-page.php';
    require_once GDC_BOOKS_PATH . 'admin/meta-boxes.php';
}
HelperReturnsUse for
plugin_dir_path( __FILE__ )Server file path with trailing slashrequire and reading files
plugin_dir_url( __FILE__ )Public URL with trailing slashEnqueuing CSS, JS and images
plugins_url( 'assets/js/app.js', __FILE__ )URL of one fileA single asset
plugin_basename( __FILE__ )gdc-books/gdc-books.phpHooks that need the plugin's identifier
Watch out: Never hard-code /wp-content/plugins/. Sites can move the plugins folder with WP_PLUGIN_DIR, and many hosts do. The helpers always return the real location.

Prefix everything

Your plugin shares one PHP global space with WordPress and every other plugin. A function called register_books() collides with the first other plugin that picked the same name, and PHP stops with a fatal error. Prefix every global name with something unique to your plugin:

KindPrefixed
Functionsgdc_books_register_post_type()
ConstantsGDC_BOOKS_VERSION
Option namesgdc_books_settings
Post meta keys_gdc_books_isbn
Hook names you creategdc_books_after_save
Script and style handlesgdc-books-admin
ClassesUse a namespace: GDC\Books\Plugin (lesson 25)
Tip: Pick a prefix of at least four characters. Two-letter prefixes like wp_ or my_ are exactly the ones everybody else picked too.

Load admin code only in the admin

The is_admin() check above means the settings page and meta box code is never even read on front-end requests. Visitors get a faster site, and admin-only code has one less way to be reached.

Practice

  • Create the folder structure above with empty files, and load them from the main file using GDC_BOOKS_PATH.
  • Print GDC_BOOKS_URL once to debug.log with error_log() and check it is a full URL.
  • Search your plugin for any function name without the gdc_books_ prefix.
Create a free account to save which lessons you've finished. Save my progress