Error: Class 'My_Service' not found
Causes:
- File not in
src/directory - File name doesn't match class name (must be
My_Service.php) - Class is abstract, interface, or trait
Fix: Check with wp di list
Error: Service 'Logger_Interface' not found
Fix: Bind in wpdi-config.php:
return array(
Logger_Interface::class => WP_Logger::class,
);Error: Circular dependency detected: ServiceA -> ServiceB -> ServiceA
Fix: Extract shared logic:
// ❌ Circular
class ServiceA {
public function __construct( ServiceB $b ) {}
}
class ServiceB {
public function __construct( ServiceA $a ) {}
}
// ✅ Fixed
class Shared_Logic {}
class ServiceA {
public function __construct( Shared_Logic $shared ) {}
}
class ServiceB {
public function __construct( Shared_Logic $shared ) {}
}Error: Cannot resolve parameter 'api_key' of type 'string'
WPDI can't autowire scalars. Use a config class:
// ❌ Can't autowire
class My_Service {
public function __construct( string $api_key ) {}
}
// ✅ Autowirable
class My_Config {
public function get_api_key(): string {
return get_option( 'api_key', '' );
}
}
class My_Service {
public function __construct( My_Config $config ) {}
}Option changes not reflected? Don't pass get_option() to constructors:
// ❌ Wrong - constructor args are cached at instantiation, not re-read
// (Don't pass get_option() to constructors — use method-level calls instead)
// ✅ Correct - fetch in methods
class My_Service {
public function get_setting(): string {
return get_option( 'setting', '' );
}
}wpdi-config.php must be next to your Scope class:
my-plugin/
├── my-plugin.php # Scope class
├── wpdi-config.php # ✅ Correct location
└── src/
└── wpdi-config.php # ❌ Wrong
Error: Cache directory is not writable: /path/to/plugin/cache
The cache/ folder needs write permissions. If using a docker environment, ensure the cache folder is mounted with :rw permissions.
DDEV
In DDEV with read-only plugin mounts, add a writable mount for the cache directory in .ddev/docker-compose.*.yaml:
services:
web:
volumes:
- ../:/var/www/html/.ddev/wordpress/wp-content/plugins/${DDEV_PROJECT}:ro
- ../cache:/var/www/html/.ddev/wordpress/wp-content/plugins/${DDEV_PROJECT}/cache:rwError: No contextual binding for 'Cache_Interface' matching parameter '$my_cache' and no 'default' binding defined
Causes:
- Parameter name doesn't match any key in the contextual binding array
- No
'default'key defined
Fix: Either add a matching '$my_cache' key or add a 'default' fallback:
return array(
Cache_Interface::class => array(
'$my_cache' => Redis_Cache::class, // Add matching key
'default' => Redis_Cache::class, // Or add a default
),
);# See all services
wp di list
# Inspect a service's dependency tree
wp di inspect Payment_Gateway
wp di ins Payment_Gateway # shorthand
# Find everything that depends on an interface or class
wp di depends Logger_Interface
wp di dep Logger_Interface # shorthand
# Clear cache
wp di clearWPDI calls opcache_invalidate() after every cache write and delete, so updates are reflected immediately when opcache is loaded. However, if validate_timestamps=0 is set in your PHP configuration (common in production), the compiled cache file may still serve stale bytecode until the next opcache_invalidate() call.
Recommendation: Include wp di compile in your deployment scripts so the cache is rebuilt and invalidated as part of each deploy.
All classes must be registered with the autoloader before Scope::boot() is called. WPDI discovers and wires classes at boot time only — classes loaded conditionally by later hooks will not be found.
If you need to wire a class that is registered after boot, add an explicit binding in wpdi-config.php instead of relying on auto-discovery.
PHP's filemtime() follows symlinks, so symlinked plugin directories work correctly for basic development workflows. However, if the target of a symlink changes (e.g., switching between branches or version directories), the cached file paths may point to stale locations.
Fix: Run wp di clear after changing symlink targets to force a full cache rebuild.
- Files in
src/directory? - File names match class names? (
My_Service.php) - Interfaces bound in
wpdi-config.php? - Contextual binding keys prefixed with
$? (e.g.'$db_cache') - No circular dependencies?
- Not passing options to constructors?
- Container initialized after
plugins_loaded? - Cache folder writable?