Skip to content

Latest commit

 

History

History
189 lines (133 loc) · 5.06 KB

File metadata and controls

189 lines (133 loc) · 5.06 KB

Troubleshooting

Class Not Found

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

Interface Not Bound

Error: Service 'Logger_Interface' not found

Fix: Bind in wpdi-config.php:

return array(
    Logger_Interface::class => WP_Logger::class,
);

Circular Dependencies

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 ) {}
}

Cannot Resolve Parameter

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 ) {}
}

Stale Options

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', '' );
    }
}

Configuration Not Loading

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

Cache Not Writable

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:rw

Contextual Binding Not Matching

Error: 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
    ),
);

Debugging

# 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 clear

Known Constraints

Opcache

WPDI 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.

Late-Binding Hooks

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.

Symlinks

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.

Quick Checklist

  • 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?