9.8 KiB
Executable File
PRAutoBlogger — Conventions
This document codifies the naming patterns, coding conventions, and step-by-step guides for extending PRAutoBlogger. Update this file whenever a new pattern is introduced.
Naming Conventions
PHP Classes
- Format:
PRAutoBlogger_{PascalCase}(e.g.,PRAutoBlogger_Content_Generator) - File naming:
class-{kebab-case}.php(e.g.,class-content-generator.php) - Interfaces:
interface-{kebab-case}.phpwithPRAutoBlogger_{Name}_Interfaceclass name
Hooks (Actions & Filters)
- Prefix: All hooks start with
prautoblogger_ - Actions:
prautoblogger_{event}(e.g.,prautoblogger_before_content_generation) - Filters:
prautoblogger_filter_{what}(e.g.,prautoblogger_filter_article_idea_score) - Hook callbacks registered in:
class-prautoblogger.php(central loader), never in the classes themselves
Database
- Table prefix:
{$wpdb->prefix}prautoblogger_(e.g.,wp_prautoblogger_source_data) - Option prefix:
prautoblogger_(e.g.,prautoblogger_openrouter_api_key) - Post meta prefix:
_prautoblogger_(underscore prefix hides from custom fields UI) - Transient prefix:
prautoblogger_(e.g.,prautoblogger_reddit_token)
Constants
- Format:
PRAUTOBLOGGER_{UPPER_SNAKE}(e.g.,PRAUTOBLOGGER_MAX_RETRIES) - Defined in:
prautoblogger.php(bootstrap file) for global constants
Hook Callback Methods
- Action callbacks: Prefixed with
on_(e.g.,on_daily_generation_triggered) - Filter callbacks: Prefixed with
filter_(e.g.,filter_content_quality)
Error Handling & Logging
Structured Logger
All logging uses PRAutoBlogger_Logger (singleton) instead of raw error_log(). The logger writes to the prab_event_log custom table and forwards errors/warnings to PHP's error_log() for server-level monitoring. The verbosity threshold is user-configurable via Settings → Publishing → Log Level.
Never call error_log() directly. Always use the Logger:
$logger = PRAutoBlogger_Logger::instance();
$logger->error( 'API call failed: ' . $error_message, 'openrouter' );
$logger->warning( 'Budget nearing limit.', 'cost-tracker' );
$logger->info( 'Pipeline complete: 3 posts generated.', 'pipeline' );
$logger->debug( 'Token counts: prompt=1200, completion=800', 'openrouter' );
Log levels (ascending verbosity)
| Level | Numeric | Use for |
|---|---|---|
| error | 0 | Failures: API errors, parse failures, DB write failures |
| warning | 1 | Degraded behavior: budget approaching, missing providers, fallback pricing |
| info | 2 | Key events: pipeline start/stop, articles generated, metrics collected |
| debug | 3 | Verbose detail: token counts, timing, skipped posts, intermediate values |
Context strings
The second argument to every log call is a short context tag identifying the origin:
pipeline, scheduler, collector, analyzer, editor, openrouter, reddit, ga4, metrics, cost-tracker, encryption.
What gets shown to the user
- API key issues → admin notice (persistent until resolved)
- Budget exceeded → admin notice + email to site admin
- Generation failures → logged to
prab_generation_logwithresponse_status = 'error' - Generation failures also shown in the admin dashboard widget
- All log entries visible in PRAutoBlogger → Activity Log (filterable by level)
What gets retried
- LLM API calls: exponential backoff, max 3 retries, then fail loudly
- Reddit API calls: exponential backoff, max 3 retries, then skip this collection cycle
- Never retry silently. Every retry is logged. Every final failure is surfaced.
Error severity levels
- Fatal: Plugin cannot function (missing API key, budget exhausted) → admin notice, stop all scheduled jobs
- Retriable: Transient API failure → retry with backoff, log each attempt
- Skippable: Single post generation failed → log, skip this post, continue with others
How To: Add a New LLM Provider
- Create
includes/providers/class-{name}-provider.php - Implement
PRAutoBlogger_LLM_Provider_Interface(seeinterface-llm-provider.php) - Required methods:
send_chat_completion(),get_available_models(),estimate_cost() - Add provider option to the settings page in
admin/class-admin-page.php - Register the provider in
class-prautoblogger.phploader - Update ARCHITECTURE.md external API integrations table
Interface contract:
interface PRAutoBlogger_LLM_Provider_Interface {
public function send_chat_completion(array $messages, string $model, array $options = []): array;
public function get_available_models(): array;
public function estimate_cost(string $model, int $prompt_tokens, int $completion_tokens): float;
public function get_provider_name(): string;
}
How To: Add a New Source Provider (Social Platform)
- Create
includes/providers/class-{platform}-provider.php - Implement
PRAutoBlogger_Source_Provider_Interface(seeinterface-source-provider.php) - Required methods:
collect_data(),get_source_type(),validate_credentials() - Add API credentials fields to the settings page in
admin/class-admin-page.php - Register the provider in
class-prautoblogger.phploader - Add the source type string to the
ab_source_data.source_typeallowed values - Update ARCHITECTURE.md file tree and external API integrations table
Interface contract:
interface PRAutoBlogger_Source_Provider_Interface {
public function collect_data(array $config): array; // Returns PRAutoBlogger_Source_Data[]
public function get_source_type(): string; // e.g., 'reddit', 'tiktok'
public function validate_credentials(): bool;
public function get_rate_limit_status(): array;
}
How To: Add a New Admin Setting
- Define the option key in the settings array in
admin/class-admin-page.php→get_settings_fields()method - Format: add an entry to the appropriate section array:
[ 'id' => 'prautoblogger_new_setting', 'label' => __('Setting Label', 'prautoblogger'), 'type' => 'text', // text, number, select, textarea, checkbox, password 'default' => '', 'description' => __('Help text shown below the field.', 'prautoblogger'), 'sanitize' => 'sanitize_text_field', // sanitization callback ] - The settings page renderer handles registration, rendering, and sanitization automatically from this array.
- Access the setting anywhere with:
get_option('prautoblogger_new_setting', $default) - For encrypted settings (API keys): use
PRAutoBlogger_Encryption::encrypt()/decrypt()
How To: Add a New Pipeline Stage
The writing pipeline is an ordered array of stages. Each stage is a class method on Content_Generator.
- Add the stage method to
class-content-generator.php:protected function stage_{name}(PRAutoBlogger_Content_Request $request): string - Register the stage in the
get_pipeline_stages()method for the appropriate mode (single_pass or multi_step) - Each stage receives the accumulated context and returns its output
- Each stage logs its cost via
Cost_Tracker::log_api_call() - If a stage fails after retries, the entire pipeline fails (no partial publishes)
How To: Add a New Frontend Component
Frontend components use wp.element (WordPress-bundled React) — no JSX, no build step.
-
Create the PHP class in
includes/frontend/class-{name}.php:- Register a shortcode via
add_shortcode()in anon_register_shortcode()method - Register a REST endpoint if the component needs async data (in
on_register_rest_route()) - Enqueue JS (
wp-elementdependency) and CSS viawp_enqueue_script/style() - Pass config to JS via
wp_localize_script()
- Register a shortcode via
-
Create JS in
assets/js/{name}.js:- Use
wp.element.createElement(aliased asel) for rendering - Import hooks:
var useState = wp.element.useState; - Read config from
window.{localizedObjectName} - Handle all states: loading, loaded, empty, error
- Mount into the shortcode's
<div id="...">mount point
- Use
-
Create CSS in
assets/css/{name}.css:- Use Peptide Starter theme CSS custom properties with fallbacks:
var(--color-text-primary, #f9fafb) - Prefix all classes with
prab-to avoid collisions - Include responsive breakpoints, focus-visible states, and loading skeletons
- Use Peptide Starter theme CSS custom properties with fallbacks:
-
Wire hooks in
class-prautoblogger.php:- Instantiate the PHP class in
register_frontend_hooks() - Register shortcode on
init, REST routes onrest_api_init
- Instantiate the PHP class in
-
Add
'frontend/'to the autoloader's$directoriesarray (already done for the first component) -
Update ARCHITECTURE.md file tree and add a data flow section
REST endpoint conventions:
- Namespace:
prautoblogger/v1 - Permission callback: use a filter hook for extensibility (e.g.,
prautoblogger_rest_{name}_public) - Sanitize all params with
sanitize_callbackin arg registration - Return only the fields the frontend needs — no unnecessary data
- Set
Cache-Controlheader for cacheable responses
Code Quality Rules
declare(strict_types=1);in every PHP file- Type declarations on all method parameters, return types, and class properties
- No file exceeds 300 lines. Split when approaching this limit
- Every class has a 3-question preamble docblock: What / Who triggers / Dependencies
- Every public method has a docblock with
@param,@return, and side effects @seereferences at the top of files that participate in multi-class flows- All strings translatable via
__()/_e()withprautobloggertext domain - No magic methods without explicit justification documented in code
- No
echoof raw data — always escape withesc_html(),esc_attr(), etc. - Nonce on every form/AJAX —
wp_nonce_field()/check_ajax_referer()