| name | storegrowth-backend-dev |
| description | Add or modify StoreGrowth (Sales Booster) backend PHP code following project conventions. Use when creating classes, services, hooks, REST controllers, ajax handlers, or modifying existing backend PHP. Read before writing backend PHP in this plugin. |
StoreGrowth Backend Development
Guidance for writing backend PHP in the StoreGrowth Sales Booster plugin. For module scaffolding see storegrowth-module-dev; for review criteria see storegrowth-code-review.
Namespace & file structure
- Root namespace:
StorePulse\StoreGrowth\ (PSR-4 → includes/). Note: NOT Dokan or WeDevs.
- Integrations:
StorePulse\StoreGrowth\Integrations\ → integrations/includes/
- Modules:
StorePulse\StoreGrowth\Modules\{ModuleName}\ → modules/{slug}/includes/ (each mapped explicitly in composer.json autoload)
- Mozart-prefixed third-party:
StorePulse\StoreGrowth\ThirdParty\Packages\ → lib/packages/. Import the DI container as StorePulse\StoreGrowth\ThirdParty\Packages\League\Container\..., never League\Container\.... Never hand-edit lib/ — it's regenerated by composer (see storegrowth-git).
- File path mirrors namespace:
StorePulse\StoreGrowth\Modules\BoGo\OrderBogo → modules/bogo/includes/OrderBogo.php.
- Every PHP file starts with
defined( 'ABSPATH' ) || exit; (or the if ( ! defined( 'ABSPATH' ) ) { exit; } form).
Conventions
- Methods / functions / variables:
snake_case (WordPress convention).
- Hook names:
spsg_ prefix for feature hooks (e.g. spsg_bogo_fly_cart_badge_enabled, spsg_modules); storegrowth_ prefix for plugin-lifecycle hooks (storegrowth_loaded, storegrowth_module_after_boot). Use spsg_ for new feature hooks.
- Options / meta keys:
spsg_ prefix (e.g. spsg_active_module_ids).
- Constants:
STOREGROWTH_* (STOREGROWTH_DIR_PATH, STOREGROWTH_VERSION, …).
- Singletons: use the
StorePulse\StoreGrowth\Traits\Singleton trait (private constructor + ::instance()).
@since tag: every new class/method/property/param/hook/route gets @since SPSG_VERSION (the literal placeholder — never a hardcoded number). The release pipeline swaps it for the real version. See storegrowth-git.
Bootstrap & lifecycle
storegrowth-sales-booster.php builds the global container, registers the root ServiceProvider, requires each module/integration bootstrap.php, then boots Bootstrap (singleton). All runtime loading happens on the woocommerce_loaded action (Bootstrap::on_wc_loaded()) — the plugin no-ops without WooCommerce. Order: modules → assets → ajax → admin → hooks → integrations.
Access the container anywhere: storegrowth_get_container(). Bootstrap also has a magic __get resolving container entries.
Dependency injection
Uses league/container v4 (mozart-prefixed). Service providers extend:
BaseServiceProvider — register-only.
BootableServiceProvider — adds an immediate boot().
Key helpers (from BaseServiceProvider) tag a class by every interface and abstract parent it has, so the container can later resolve "all X":
$this->add_with_implements_tags( $id, $concrete, $shared = false );
$this->share_with_implements_tags( $id, $concrete );
This is the mechanism behind auto-registration below.
Registering hooks — the HookRegistry pattern
To register WordPress hooks, implement StorePulse\StoreGrowth\Interfaces\HookRegistry. Bootstrap::register_hooks() resolves everything tagged HookRegistry and calls register_hooks() automatically:
namespace StorePulse\StoreGrowth\Modules\BoGo;
use StorePulse\StoreGrowth\Traits\Singleton;
use StorePulse\StoreGrowth\Interfaces\HookRegistry;
class EnqueueScript implements HookRegistry {
use Singleton;
public function register_hooks(): void {
add_action( 'wp_enqueue_scripts', [ $this, 'front_scripts' ] );
add_filter( 'spsg_some_filter', [ $this, 'handle' ] );
}
}
Register the class in a BootstrapServiceProvider via share_with_implements_tags( EnqueueScript::class ) and it loads automatically when the owning module is active.
REST API controllers
Extend WP_REST_Controller. Bootstrap::register_rest_routes() resolves everything tagged WP_REST_Controller (on rest_api_init) and calls register_routes() — so just registering the controller in a provider wires it up.
- Namespace:
sales-booster/v1. Set in the constructor: $this->namespace = 'sales-booster/v1'; $this->rest_base = 'bogo/offers';
- Always provide a
permission_callback on every route (e.g. [ $this, 'check_permission' ] returning a capability check like current_user_can( 'manage_options' )).
- Sanitize every
$request param (absint, sanitize_text_field, wc_clean, …); define args with sanitize_callback/validate_callback.
- The plugin also extends WC's product controller:
includes/REST/ProductController.php (sales-booster/v1, product picker for the settings UI).
namespace StorePulse\StoreGrowth\Modules\BoGo\REST;
use WP_REST_Controller;
use WP_REST_Server;
class BogoController extends WP_REST_Controller {
public function __construct() {
$this->namespace = 'sales-booster/v1';
$this->rest_base = 'bogo/offers';
}
public function register_routes(): void {
register_rest_route( $this->namespace, '/' . $this->rest_base, [
[
'methods' => WP_REST_Server::READABLE,
'callback' => [ $this, 'get_items' ],
'permission_callback' => [ $this, 'check_permission' ],
'args' => $this->get_collection_params(),
],
] );
}
}
Ajax
Admin-ajax handlers live in includes/Ajax.php (container alias ajax-service, loaded only when DOING_AJAX). Actions use the spsg_ prefix, e.g. add_action( 'wp_ajax_spsg_admin_ajax', ... ). New REST-style data should prefer a REST controller; ajax is for the existing admin-ajax surface.
i18n
- Text domain:
storegrowth-sales-booster for ALL translatable strings.
- Use
__(), esc_html__(), esc_attr__(), _e(), _n(), _x() with the text domain.
- Add
/* translators: */ comments before any sprintf() with placeholders. Never concatenate translated strings — use sprintf().
- Regenerate the POT with
npm run makepot.
Helpers
StorePulse\StoreGrowth\Helper — static helpers: Helper::get_plugin_assets_url(), Helper::get_modules_url(), Helper::get_modules_path().
helpers/functions.php — global procedural helpers (composer files autoload).
Coding standards
- Ruleset
phpcs.xml: WordPress + PHPCompatibilityWP + WooCommerce-Core, text domain enforced.
- Run
composer phpcs (and composer phpcbf to auto-fix). PRs run PHPCS on changed files (.github/workflows/phpcs.yml).
- PHP 7.4+. Use strict comparisons (
===/!==) and in_array( $needle, $haystack, true ).
Key reference files
storegrowth-sales-booster.php — entry point, container bootstrap, module/integration includes
includes/Bootstrap.php — lifecycle, hook & REST auto-registration
includes/DependencyManagement/BaseServiceProvider.php — *_with_implements_tags() helpers
includes/DependencyManagement/Providers/ServiceProvider.php — root provider
includes/Interfaces/HookRegistry.php, includes/Interfaces/ModuleSkeleton.php
includes/Helper.php, helpers/functions.php