Developer guide

Architecture

How PeoplePress boots, and the order in which its subsystems become available.

This area is still changing. Steps and screenshots may lag behind the current release.

PeoplePress does almost nothing at file load. The entry point registers an autoloader, boots a service container, and then hands control to a series of bootstrappers. Everything an addon can hook into exists because one of those bootstrappers put it there — which is why boot order, not file structure, is the thing to understand first.

The entry point

peoplepress.php runs five things, in this order:

  1. require 'vendor/autoload.php' — Composer dependencies.
  2. require_once 'src/app/bootstrap/pp-hooks-mappings.php' — maps WordPress hooks onto PeoplePress-specific action hooks. This is what makes the later lifecycle hooks fire at predictable points in the WordPress request.
  3. Registers PP_Autoloader for the PeoplePress\ namespace against src/.
  4. PeoplePress::boot() — sets up the container: module manager, task manager, process manager, and the other global services, exposed as read-only properties on the PeoplePress singleton.
  5. The bootstrappers.

Boot order

PeoplePress::boot();          // service container
PP_Core_Bootstrapper::boot(); // core functions, module lifecycle hooks
PP_Bootstrapper::boot();      // shortcodes, UI, privacy levels, nav items

PP_Core_Bootstrapper is the one that matters for extension: it loads core function files and registers the module lifecycle hooks. Nothing that depends on a module being registered can run before it.

Notification, background process, async task, and admin bootstrappers follow.

Source: peoplepress.php, class-peoplepress.php, src/app/bootstrap/class-pp-core-bootstrapper.php

Why a container and not globals

Modules need to reach each other — the activity module reads member data, the notification module reacts to almost everything — and WordPress offers no structure for that beyond globals. The PeoplePress singleton exposes services through a read-only magic getter, so a module can depend on a service without being able to swap it out from under another module.

The trade-off: service resolution is not type-checked, so a typo in a service name is a runtime error rather than a static one.

Where addons attach

Third-party modules register on the pp_register_modules action, which fires on plugins_loaded at priority 10100. By that point core has booted and the module registry exists, so a module registered there participates in the same lifecycle as a core one.

TODO(verify): the full lifecycle method sequence (initialize, load, setup, init, rest_api_init, register_item_types, register_extensions) against src/core/module/class-pp-module.php before this is promoted out of draft.

Request flow

TODO(verify): PP_Item_Matcher parses requests on the do_parse_request filter, the wp action sets query state, and template_redirect dispatches through PP_Route_Dispatcher. Confirm each against src/core/pp-routing-functions.php before publishing.

On this page