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: