WPZylos Framework Rules

Compatibility Targets

  • PHP: 8.1+ minimum
  • WordPress: 6.0+ minimum

Dependency Graph (enforced, no cycles)

core ← nothing
container ← psr/container
hooks, security, config, i18n, database, cli-core ← core
http ← core + security
routing ← core + http + container
validation, views ← core + i18n
migrations ← core + database
wp-cli ← core + migrations (WP_CLI only)
cli-devtool ← cli-core + symfony/console

Architecture Rules

1. No Global Framework Constants

All identity via PluginContext. No define() in framework packages.

2. ContextInterface Pattern

  • Framework depends on ContextInterface
  • Plugin owns PluginContext implementation
  • Survives PHP-Scoper namespace rewriting

3. Hook Prefixing

  • wpAction/wpFilter: WordPress core hooks, NEVER prefixed
  • action/filter/doAction/applyFilter: Custom hooks, ALWAYS prefixed

4. Localization

  • All user-facing strings via $context->textDomain()
  • No hardcoded text domains in packages

5. Security Requirements

Every state-changing action requires:

  1. Capability check first (current_user_can())
  2. Nonce verification second (wp_verify_nonce())

Sanitization:

  • Sanitize input per field (not blanket)
  • Escape output by default (e, ea, eu, kses)

6. Database Safety

  • All queries via $wpdb->prepare()
  • Table names via $context->tableName() (site/network aware)

7. Rewrite Rules

  • Flush ONLY on activation/deactivation
  • NEVER at runtime

8. PHP-Scoper Safety

  • No hardcoded framework namespaces
  • Entry file uses plugin-owned classes only
  • Framework accessed via container or interface contracts

9. No Hidden Globals

  • No global singletons
  • Bind everything in container

Gated Dispatch Rules

Dispatch frontend routes only when ALL conditions met:

  • !is_admin()
  • !wp_doing_cron()
  • !wp_doing_ajax()
  • !(defined('REST_REQUEST') && REST_REQUEST)
  • Router match found

Override via filter:

apply_filters($context->hook('should_dispatch'), true, $request)

Activation Rules

Activation MAY load:

  • PluginContext
  • WPAdapter (for rewrite registration)
  • Route callable (to extract patterns)

Activation MUST NOT boot:

  • Container
  • Service providers
  • Config/Views/Validation/etc.

Escaping Enforcement

  • PHP templates: output ONLY through e()/ea()/eu()/kses()
  • Twig: autoescape: 'html' ON by default
  • Raw output: explicitly marked, strongly discouraged

Multisite Rules

  • Migration state: wp_options (site) or wp_sitemeta (network)
  • Table naming: $context->tableName($name, 'site'|'network')
  • Uninstall: check activation mode, clean correct storage

Plugin Header Requirements

/**
 * Plugin Name: My Plugin
 * Version: 1.0.0
 * Requires at least: 6.0
 * Requires PHP: 8.1
 * Text Domain: my-plugin
 * Domain Path: /resources/lang
 */