System Troubleshooting

Top 20 ecosystem-wide issues and solutions.


1. PHP-Scoper: Class Not Found After Build

Problem: Built plugin throws Class 'X' not found.

Cause: Scoper prefixed references WordPress/internal classes.

Fix: Exclude in scoper.inc.php:

return [
    'exclude-namespaces' => [
        'WPZylos\Framework\Core\Contracts',
    ],
    'exclude-classes' => [
        'WP_Query', 'WP_Post', 'wpdb',
    ],
];

2. PHP-Scoper: WordPress Functions Prefixed

Problem: PrefixedVendor\add_action() called.

Cause: Scoper prefixed WordPress functions.

Fix: Exclude WordPress functions:

'exclude-functions' => [
    'add_action', 'add_filter', 'wp_enqueue_*',
    'get_option', 'update_option', 'delete_option',
    // ... all WP functions
],

3. Multiple Plugins: Hook Collision

Problem: Two WPZylos plugins interfere with each other.

Cause: Same custom hook names without prefix.

Fix: Always use context for custom hooks:

// Bad: Collision - both use 'data_saved'
do_action('data_saved', $data);

// Good: Isolated - uses myplugin_data_saved
$hooks->doAction('data_saved', $data);

4. Activation: Full Application Boot

Problem: Activation fails or is very slow.

Cause: Boot loads full application during activation.

Fix: Lightweight activation only:

register_activation_hook(__FILE__, function () {
    // Only run migrations
    $migrator->run(__DIR__ . '/database/migrations');

    // Flush rewrite rules ONCE
    flush_rewrite_rules();
});

5. Container: Circular Dependency

Problem: Container hangs or stack overflow.

Fix: Break cycle with setter or factory:

$container->singleton(B::class, function ($c) {
    $b = new B();
    $b->setA($c->get(A::class));
    return $b;
});

6. Database: SQL Injection

Problem: Security vulnerability in queries.

Fix: Always use $wpdb->prepare():

$db->getResults($db->prepare(
    "SELECT * FROM {$table} WHERE id = %d",
    absint($id)
));

7. Views: XSS Vulnerability

Problem: User content injected as HTML.

Fix: Escape all output:

echo esc_html($title);
echo esc_attr($value);
echo esc_url($link);
echo wp_kses_post($content);

8. Multisite: Wrong Table Prefix

Problem: Tables created on main site, not subsite.

Fix: Use context for table names:

$table = $context->tableName('orders', 'site');
// Returns: wp_2_myplugin_orders for subsite 2

9. WP-CLI: Commands Not Found

Problem: wp zylos not recognized.

Fix: Verify WP-CLI detection:

if (defined('WP_CLI') && WP_CLI) {
    $adapter->register($commands);
}

10. Config: Environment Override Not Working

Problem: Local config doesn't apply.

Fix: Set WP_ENVIRONMENT_TYPE:

// wp-config.php
define('WP_ENVIRONMENT_TYPE', 'local');

11. Validation: Rule Not Recognized

Problem: Unknown rule: custom

Fix: Register custom rules first:

$validator->extend('custom', function ($value) {
    return $value === 'expected';
});

12. Migrations: dbDelta Not Creating Columns

Problem: Table structure wrong after migration.

Fix: Follow dbDelta formatting rules:

// Two spaces before (id) in PRIMARY KEY
"PRIMARY KEY  (id)"

13. Hooks: Callback Never Fires

Problem: add_action callback not executing.

Fix: Register before hook fires:

// Too late: 'init' already fired
add_action('plugins_loaded', function () {
    // Boot registers 'init' hooks here
    $app->boot();
}, 10);

14. I18n: Translations Not Loading

Problem: Text shows English only.

Fix: Load text domain at correct time:

$hooks->wpAction('init', function () use ($context) {
    load_plugin_textdomain(
        $context->textDomain(),
        false,
        dirname(plugin_basename($context->file())) . '/languages'
    );
});

15. AJAX: Returns 0

Problem: AJAX response is just 0.

Fix: Action name must match:

// PHP: wp_ajax_myplugin_action
$router->ajax('action', [Controller::class, 'handle']);

// JS: must use prefixed action
fetch(ajaxurl, {
    body: new URLSearchParams({
        action: 'myplugin_action'
    })
});

16. Nonce: Verification Fails

Problem: Valid form rejected.

Fix: Match action names exactly:

// Create
$token = $nonce->create('save_settings');

// Verify (must match 'save_settings')
$nonce->verify($_POST['_nonce'], 'save_settings');

17. Logger: Permission Denied

Problem: Can't write to log file.

Fix: Create directory first:

$logDir = $context->path('logs');
wp_mkdir_p($logDir);

18. Events: Listener Not Called

Problem: Event dispatched but handler skipped.

Fix: Match exact event class:

$provider->addListener(ConcreteEvent::class, $handler);
$dispatcher->dispatch(new ConcreteEvent()); // Matches

19. Routing: 404 on Admin Page

Problem: Menu page shows "not allowed".

Fix: Register routes before admin_menu:

$hooks->wpAction('admin_menu', [$this, 'registerRoutes'], 5);

20. Build: Missing vendor

Problem: Production build missing dependencies.

Fix: Include vendor in build:

composer install --no-dev --optimize-autoloader
php vendor/bin/php-scoper add-prefix
cd build && composer dump-autoload

Package-Specific Troubleshooting

Each package has its own troubleshooting guide: