A powerful dependency injection container specifically designed for WordPress plugin development. This container provides advanced features like auto-wiring, service binding, configuration management, and service tagging to help you build maintainable and testable WordPress plugins.
- 🚀 Dependency Injection: Automatic dependency resolution and injection
- 📦 Service Binding: Bind interfaces to implementations
- đź”§ Configuration Management: Built-in configuration handling with dot notation
- 🏷️ Service Tagging: Group and retrieve related services
- đź’ˇ Auto-wiring: Automatic class instantiation and dependency resolution
- 🎯 Singleton Support: Register shared instances across your application
- 🔄 Alias System: Create shortcuts for your services
- 🎪 WordPress Integration: Designed specifically for WordPress plugin architecture
Install via Composer:
composer require byteever/container
Or add to your composer.json
:
{
"require": {
"byteever/container": "^1.0"
}
}
use ByteEver\Container\Container;
// Create a container instance
$container = Container::create();
// Bind a service
$container->bind('logger', MyLogger::class);
// Resolve the service
$logger = $container->make('logger');
// Register an instance
$container->instance('database', new Database($config));
// Get the instance
$db = $container->get('database');
Important: For WordPress plugin development, you must pass the main plugin file path to extract plugin metadata.
// In your main plugin file
use ByteEver\Container\Container;
class MyPlugin {
private Container $container;
public function __construct() {
// REQUIRED: Pass the main plugin file (__FILE__)
$this->container = Container::create(__FILE__);
$this->registerServices();
}
private function registerServices(): void {
// Register your plugin services
$this->container->singleton('admin', Admin::class);
$this->container->singleton('frontend', Frontend::class);
$this->container->bind('api', API::class);
}
public function getContainer(): Container {
return $this->container;
}
}
The container includes a powerful configuration system with dot notation support:
$container = Container::create([
'database' => [
'host' => 'localhost',
'port' => 3306,
'credentials' => [
'username' => 'admin',
'password' => 'secret'
]
],
'cache' => [
'driver' => 'redis',
'ttl' => 3600
]
]);
// Get configuration values
$host = $container->get_config('database.host'); // 'localhost'
$username = $container->get_config('database.credentials.username'); // 'admin'
$ttl = $container->get_config('cache.ttl', 1800); // 3600 (with default fallback)
// Set configuration values
$container->set_config('api.key', 'your-api-key');
$container->set_config('features.beta', true);
// Check if configuration exists
if ($container->has_config('database.credentials')) {
// Configuration exists
}
// Bind a concrete class
$container->bind('logger', FileLogger::class);
// Bind with a closure
$container->bind('mailer', function($container) {
return new Mailer($container->get_config('smtp'));
});
// Bind interface to implementation
$container->bind(LoggerInterface::class, FileLogger::class);
// Register as singleton
$container->singleton('database', Database::class);
// Both calls return the same instance
$db1 = $container->make('database');
$db2 = $container->make('database');
// $db1 === $db2 (true)
// Register existing instance
$logger = new Logger('/path/to/log');
$container->instance('logger', $logger);
// Auto-instantiation (new feature!)
$container->instance(Database::class); // Automatically creates and registers instance
// Bulk registration
$container->instance([
'cache' => new RedisCache(),
'session' => new SessionManager()
]);
The container automatically resolves dependencies through constructor injection:
class UserService {
public function __construct(
private Database $database,
private Logger $logger,
private CacheInterface $cache
) {}
}
class Database {
public function __construct(private string $host = 'localhost') {}
}
// Register dependencies
$container->bind(CacheInterface::class, RedisCache::class);
$container->instance('logger', new Logger());
// Auto-wiring in action - resolves all dependencies automatically
$userService = $container->make(UserService::class);
Group related services with tags:
// Tag services
$container->tag('handlers', [
'user_handler',
'post_handler',
'comment_handler'
]);
// Or tag individually
$container->bind('user_handler', UserHandler::class);
$container->tag('handlers', 'user_handler');
// Get all services with a tag
$handlers = $container->tagged('handlers');
foreach ($handlers as $handler) {
$handler->process();
}
Create convenient shortcuts for your services:
$container->bind(DatabaseInterface::class, MySQLDatabase::class);
$container->alias(DatabaseInterface::class, 'db');
// Now you can use either
$database = $container->make(DatabaseInterface::class);
$database = $container->make('db'); // Same instance
// The container can extract plugin metadata
$container = Container::create(__FILE__); // Pass your main plugin file
// Access plugin information
$pluginName = $container->get_config('name');
$version = $container->get_config('version');
$textDomain = $container->get_config('text_domain');
class MyPlugin {
public function __construct() {
$this->container = Container::create(__FILE__);
$this->registerHooks();
}
private function registerHooks(): void {
add_action('init', [$this, 'init']);
add_action('admin_init', [$this, 'adminInit']);
}
public function init(): void {
$frontend = $this->container->make('frontend');
$frontend->init();
}
public function adminInit(): void {
$admin = $this->container->make('admin');
$admin->init();
}
}
class DatabaseServiceProvider {
public function register(Container $container): void {
$container->singleton('database', function($container) {
return new Database(
$container->get_config('database.host'),
$container->get_config('database.port'),
$container->get_config('database.credentials')
);
});
$container->alias('database', 'db');
}
}
// Register the service provider
$provider = new DatabaseServiceProvider();
$provider->register($container);
// Bind different implementations based on environment
if ($container->get_config('app.debug')) {
$container->bind(LoggerInterface::class, DebugLogger::class);
} else {
$container->bind(LoggerInterface::class, ProductionLogger::class);
}
// You can pass additional parameters when resolving
$userService = $container->make(UserService::class, [
'connection' => 'mysql-read-replica'
]);
Container::create(array|string $config = [], string $namespace = __NAMESPACE__): static
make(string $abstract, array $parameters = []): mixed
get(string $key, mixed $default = null): mixed
bind(string $abstract, Closure|string|null $concrete = null, bool $shared = false): self
singleton(string $abstract, Closure|string|null $concrete = null): self
instance(string|array $abstract, object $instance = null): self
get_config(string $key, mixed $default = null): mixed
set_config(string $key, mixed $value): self
has_config(string $key): bool
bound(string $abstract): bool
alias(string $abstract, string $alias): void
tag(string $tag, array|string $services): void
tagged(string $tag): array
flush(): void
// Good
$container->bind(LoggerInterface::class, FileLogger::class);
// Instead of
$container->bind('logger', FileLogger::class);
class MyPlugin {
public function __construct() {
$this->container = Container::create(__FILE__);
$this->registerServices(); // Do this early
$this->registerHooks();
}
}
class PluginServiceProvider {
public function register(Container $container): void {
// Group related service registrations
$this->registerDatabase($container);
$this->registerCache($container);
$this->registerLogging($container);
}
}
// Let the container handle dependencies
class AdminController {
public function __construct(
private UserRepository $users,
private Logger $logger,
private Validator $validator
) {}
}
// Just resolve - dependencies are auto-injected
$controller = $container->make(AdminController::class);
The container makes testing easier by allowing dependency injection:
class UserServiceTest extends TestCase {
public function testUserCreation(): void {
$container = Container::create();
// Mock dependencies
$mockDatabase = $this->createMock(Database::class);
$mockLogger = $this->createMock(Logger::class);
$container->instance('database', $mockDatabase);
$container->instance('logger', $mockLogger);
$userService = $container->make(UserService::class);
// Test your service with mocked dependencies
$this->assertInstanceOf(UserService::class, $userService);
}
}
// Main plugin class
class MyAwesomePlugin {
private Container $container;
public function __construct() {
$this->container = Container::create(__FILE__);
$this->bootstrap();
}
private function bootstrap(): void {
// Register core services
$this->registerCore();
// Register WordPress hooks
$this->registerHooks();
// Initialize modules
$this->initializeModules();
}
private function registerCore(): void {
$this->container->singleton('plugin', $this);
$this->container->singleton('loader', HookLoader::class);
$this->container->singleton('admin', AdminModule::class);
$this->container->singleton('frontend', FrontendModule::class);
$this->container->singleton('api', APIModule::class);
}
private function registerHooks(): void {
$loader = $this->container->make('loader');
$loader->run();
}
private function initializeModules(): void {
if (is_admin()) {
$this->container->make('admin')->init();
} else {
$this->container->make('frontend')->init();
}
}
}
- PHP 7.4 or higher
- WordPress 5.0 or higher (for WordPress-specific features)
We welcome contributions! Please see our Contributing Guide for details.
This project is licensed under the MIT License - see the LICENSE file for details.
- Documentation: Full Documentation
- Issues: GitHub Issues
- Discussions: GitHub Discussions
See CHANGELOG.md for a list of changes.
ByteEver Container - Powering modern WordPress plugin development with clean, maintainable dependency injection.