|
| 1 | +# Modules and Initialization |
| 2 | + |
| 3 | +## Overview |
| 4 | +The WP Framework organizes functionality into small Modules. A Module is any class that implements TenupFramework\ModuleInterface and typically uses the TenupFramework\Module trait. Modules are discovered at runtime and initialized in a defined order. |
| 5 | + |
| 6 | +Key interfaces and utilities: |
| 7 | +- `TenupFramework\ModuleInterface`: declares `load_order()`, `can_register()`, `register()`. |
| 8 | +- `TenupFramework\Module` trait: provides a default `load_order()` of `10` and leaves `can_register()` and `register()` abstract for your class to implement. |
| 9 | +- `TenupFramework\ModuleInitialization`: discovers, orders, and initializes your Modules. |
| 10 | + |
| 11 | +## Bootstrapping |
| 12 | +Call the initializer at plugin or theme bootstrap, pointing it at the directory containing your namespaced classes (e.g., `inc/` or `src/`): |
| 13 | + |
| 14 | +```php |
| 15 | +use TenupFramework\ModuleInitialization; |
| 16 | + |
| 17 | +ModuleInitialization::instance()->init_classes( YOUR_PLUGIN_INC ); |
| 18 | +``` |
| 19 | + |
| 20 | +`YOUR_PLUGIN_INC` (or your equivalent constant/path) should resolve to an existing directory. If it does not exist, a RuntimeException will be thrown. |
| 21 | + |
| 22 | +## How discovery and initialization work |
| 23 | +ModuleInitialization performs the following steps: |
| 24 | +1. Validate the directory exists; otherwise throw a RuntimeException. |
| 25 | +2. Discover class names within the directory using spatie/structure-discoverer. |
| 26 | + - In production and staging environments (wp_get_environment_type), results are cached for performance using a file-based cache. |
| 27 | + - Caching is skipped entirely when the constant `VIP_GO_APP_ENVIRONMENT` is defined. |
| 28 | +3. Reflect on each discovered class and skip any that: |
| 29 | + - are not instantiable, |
| 30 | + - do not implement `TenupFramework\ModuleInterface`. |
| 31 | +4. Instantiate the class. |
| 32 | +5. Fire an action before registration for each module: `tenup_framework_module_init__{slug}` |
| 33 | + - `slug` is the sanitized class FQN (backslashes replaced with dashes, then passed through `sanitize_title`). |
| 34 | +6. Sort modules by `load_order()` (lower numbers first) and iterate in order. |
| 35 | +7. For each module, call `register()` only if `can_register()` returns true. |
| 36 | +8. Store initialized modules for later retrieval. |
| 37 | + |
| 38 | +Environment cache behavior |
| 39 | +- Where cache lives: under the directory you pass to `init_classes()`, in a `class-loader-cache` folder (e.g., `YOUR_PLUGIN_INC . 'class-loader-cache'`). |
| 40 | +- When it’s used: only in `production` and `staging` environment types (`wp_get_environment_type()`). |
| 41 | +- How to clear: delete the `class-loader-cache` folder; it will be rebuilt on next discovery. |
| 42 | +- How to disable in development: use `development` or `local` environment types, or define `VIP_GO_APP_ENVIRONMENT` to skip the cache. |
| 43 | + |
| 44 | +Hooks |
| 45 | +- Action: `tenup_framework_module_init__{slug}` — fires before each module’s `register()` runs. |
| 46 | + - Parameters: the module instance. |
| 47 | + - Example: |
| 48 | + ```php |
| 49 | + add_action( 'tenup_framework_module_init__yourvendor-yourplugin-features-frontendtweaks', function ( $module ) { |
| 50 | + // Inspect or adjust before register() |
| 51 | + } ); |
| 52 | + ``` |
| 53 | + |
| 54 | +Load order dependencies example |
| 55 | +- If Module B depends on Module A: |
| 56 | + ```php |
| 57 | + class ModuleA implements ModuleInterface { use Module; public function load_order(): int { return 5; } } |
| 58 | + class ModuleB implements ModuleInterface { use Module; public function load_order(): int { return 10; } } |
| 59 | + ``` |
| 60 | + Lower numbers run first. Taxonomies typically use 9 so post types (default 10) can associate afterward. |
| 61 | + |
| 62 | +Utilities: |
| 63 | +- `ModuleInitialization::get_module( $classFqn )` retrieves an initialized module instance by its fully qualified class name. |
| 64 | +- `ModuleInitialization::instance()->get_all_classes()` returns all initialized module instances keyed by slug. |
| 65 | + |
| 66 | +## Module lifecycle in your code |
| 67 | +Your Module should be lightweight at construction time. Use the following methods effectively: |
| 68 | +- `load_order(): int` — controls initialization order (default = 10 via Module trait). Override to run earlier/later. For example, taxonomy modules may run at 9 so they are available before post types. |
| 69 | +- `can_register(): bool` — return true only when the module should register hooks in the current context (e.g., only in admin, only on frontend, only if a feature flag is enabled). |
| 70 | +- `register(): void` — attach your WordPress hooks/filters and perform setup here. |
| 71 | + |
| 72 | +### Example |
| 73 | +```php |
| 74 | +namespace YourVendor\YourPlugin\Features; |
| 75 | + |
| 76 | +use TenupFramework\ModuleInterface; |
| 77 | +use TenupFramework\Module; |
| 78 | + |
| 79 | +class FrontendTweaks implements ModuleInterface { |
| 80 | + use Module; // default load_order() = 10 |
| 81 | + |
| 82 | + public function can_register(): bool { |
| 83 | + return ! is_admin(); // only on frontend |
| 84 | + } |
| 85 | + |
| 86 | + public function register(): void { |
| 87 | + add_action( 'wp_enqueue_scripts', [ $this, 'enqueue' ] ); |
| 88 | + } |
| 89 | + |
| 90 | + public function enqueue(): void { |
| 91 | + // ... enqueue assets here ... |
| 92 | + } |
| 93 | +} |
| 94 | +``` |
| 95 | + |
| 96 | +## Troubleshooting |
| 97 | +- Directory is required — If `init_classes()` is called with an empty or non-existent directory, a RuntimeException is thrown. |
| 98 | +- Class not initialized — Ensure the class is instantiable and implements `TenupFramework\ModuleInterface`. |
| 99 | +- Order of initialization — If you have inter-module dependencies, adjust `load_order()` to ensure prerequisites are registered first. |
| 100 | +- Observability — Use the `tenup_framework_module_init__{slug}` action to inspect or modify module instances before they register. |
0 commit comments