gacela-project/container
Composer 安装命令:
composer require gacela-project/container
包简介
A minimalistic container dependency resolver
关键字:
README 文档
README
A minimalistic, PSR-11 compliant dependency injection container with automatic constructor injection and zero configuration.
Features
- 🚀 Zero Configuration: Automatic constructor injection without verbose setup
- 🔄 Circular Dependency Detection: Clear error messages when dependencies form a loop
- 📦 PSR-11 Compliant: Standard container interface for interoperability
- ⚡ Performance Optimized: Built-in caching and warmup capabilities
- 🔍 Introspection: Debug and inspect container state easily
- 🎯 Type Safe: Requires type hints for reliable dependency resolution
- 🏷️ PHP 8 Attributes: Declarative configuration with #[Inject], #[Singleton], and #[Factory]
Installation
composer require gacela-project/container
Quick Start
Basic Usage
use Gacela\Container\Container; // Simple auto-wiring $container = new Container(); $instance = $container->get(YourClass::class);
With Bindings
Map interfaces to concrete implementations:
$bindings = [ LoggerInterface::class => FileLogger::class, CacheInterface::class => new RedisCache('localhost'), ConfigInterface::class => fn() => loadConfig(), ]; $container = new Container($bindings); $logger = $container->get(LoggerInterface::class); // Returns FileLogger
Contextual Bindings
Different implementations based on which class needs them:
// UserController gets FileLogger, AdminController gets DatabaseLogger $container->when(UserController::class) ->needs(LoggerInterface::class) ->give(FileLogger::class); $container->when(AdminController::class) ->needs(LoggerInterface::class) ->give(DatabaseLogger::class); // Multiple classes can share the same contextual binding $container->when([ServiceA::class, ServiceB::class]) ->needs(CacheInterface::class) ->give(RedisCache::class);
PHP 8 Attributes
Use attributes for declarative dependency configuration:
#[Inject] - Specify Implementation
Override type hints to inject specific implementations:
use Gacela\Container\Attribute\Inject; class NotificationService { public function __construct( #[Inject(EmailLogger::class)] private LoggerInterface $logger, ) {} } // EmailLogger will be injected even if LoggerInterface is bound to FileLogger $service = $container->get(NotificationService::class);
#[Singleton] - Single Instance
Mark a class to be instantiated only once:
use Gacela\Container\Attribute\Singleton; #[Singleton] class DatabaseConnection { public function __construct(private string $dsn) {} } $conn1 = $container->get(DatabaseConnection::class); $conn2 = $container->get(DatabaseConnection::class); // $conn1 === $conn2 (same instance)
#[Factory] - New Instances
Always create fresh instances:
use Gacela\Container\Attribute\Factory; #[Factory] class RequestContext { public function __construct(private LoggerInterface $logger) {} } $ctx1 = $container->get(RequestContext::class); $ctx2 = $container->get(RequestContext::class); // $ctx1 !== $ctx2 (different instances)
Performance Note: Attribute checks are cached internally, so repeated instantiations of the same class avoid expensive reflection operations, providing 15-20% performance improvement.
How It Works
The container automatically resolves dependencies based on type hints:
- Primitive types: Uses default values (must be provided)
- Classes: Instantiates and resolves dependencies recursively
- Interfaces: Resolves using bindings defined in the container
Example
class UserService { public function __construct( private UserRepository $repository, private LoggerInterface $logger, ) {} } class UserRepository { public function __construct(private PDO $pdo) {} } // Setup $bindings = [ LoggerInterface::class => FileLogger::class, PDO::class => new PDO('mysql:host=localhost;dbname=app', 'user', 'pass'), ]; $container = new Container($bindings); // Auto-resolves UserService -> UserRepository -> PDO $service = $container->get(UserService::class);
Advanced Features
Factory Services
Create new instances on every call:
$factory = $container->factory(fn() => new TempFile()); $container->set('temp_file', $factory); $file1 = $container->get('temp_file'); // New instance $file2 = $container->get('temp_file'); // Different instance
Extending Services
Wrap or modify services (even before they're created):
$container->set('logger', fn() => new FileLogger('/var/log/app.log')); $container->extend('logger', function ($logger, $container) { return new LoggerDecorator($logger); });
Protecting Closures
Prevent closures from being executed:
$closure = fn() => 'Hello World'; $container->set('greeting', $container->protect($closure)); $result = $container->get('greeting'); // Returns the closure itself
Resolving Callables
Automatically inject dependencies into any callable:
$result = $container->resolve(function (LoggerInterface $logger, CacheInterface $cache) { $logger->info('Cache cleared'); return $cache->clear(); });
Service Introspection
Debug and inspect container state:
// Get all registered service IDs $services = $container->getRegisteredServices(); // Check if service is a factory if ($container->isFactory('temp_file')) { // Returns new instance each time } // Check if service is frozen (accessed) if ($container->isFrozen('logger')) { // Cannot be modified anymore } // Get all bindings $bindings = $container->getBindings(); // Get comprehensive statistics $stats = $container->getStats(); /* [ 'registered_services' => 42, 'frozen_services' => 15, 'factory_services' => 3, 'bindings' => 8, 'cached_dependencies' => 25, 'memory_usage' => '2.34 MB' ] */
Performance Optimization
Pre-resolve dependencies for faster runtime:
// During application bootstrap $container->warmUp([ UserService::class, OrderService::class, PaymentProcessor::class, ]); // Later requests benefit from cached dependency resolution $service = $container->get(UserService::class); // Faster!
Service Aliasing
Create multiple names for the same service:
// Create an alias $container->alias('db', PDO::class); // Access via alias or original name $db1 = $container->get('db'); // Same instance $db2 = $container->get(PDO::class); // Same instance // Alias resolution is cached for optimal performance
API Reference
Container Methods
| Method | Description |
|---|---|
get(string $id): mixed |
Retrieve or create a service |
has(string $id): bool |
Check if service exists |
set(string $id, mixed $instance): void |
Register a service |
remove(string $id): void |
Remove a service |
resolve(callable $callable): mixed |
Execute callable with dependency injection |
factory(Closure $instance): Closure |
Mark service as factory (new instance each time) |
extend(string $id, Closure $instance): Closure |
Wrap/modify a service |
protect(Closure $instance): Closure |
Prevent closure execution |
getRegisteredServices(): array |
Get all service IDs |
isFactory(string $id): bool |
Check if service is a factory |
isFrozen(string $id): bool |
Check if service is frozen |
getBindings(): array |
Get all bindings |
warmUp(array $classNames): void |
Pre-resolve dependencies |
alias(string $alias, string $id): void |
Create an alias for a service (with caching) |
getStats(): array |
Get container statistics for debugging and performance monitoring |
when(string|array $concrete): ContextualBindingBuilder |
Define contextual bindings for specific classes |
Static Methods
// Quick instantiation without container setup $instance = Container::create(YourClass::class);
Best Practices
1. Use Constructor Injection
// Good class UserController { public function __construct( private UserService $userService, private LoggerInterface $logger ) {} } // Avoid setter injection (not supported)
2. Always Use Type Hints
// Good - type hint required public function __construct(LoggerInterface $logger) {} // Bad - will throw exception public function __construct($logger) {}
3. Provide Default Values for Scalars
// Good public function __construct( UserRepository $repo, int $maxRetries = 3, string $env = 'production' ) {} // Bad - scalars without defaults cannot be resolved public function __construct(string $apiKey) {} // Exception!
4. Use Bindings for Interfaces
// Always bind interfaces to implementations $bindings = [ LoggerInterface::class => FileLogger::class, CacheInterface::class => RedisCache::class, ];
5. Warm Up in Production
// In your bootstrap file $container->warmUp([ // List frequently used services UserService::class, AuthService::class, Router::class, ]);
Error Handling
The container provides clear, actionable error messages with helpful suggestions:
Missing Type Hint
No type hint found for parameter '$logger'.
Type hints are required for dependency injection to work properly.
Add a type hint to the parameter, for example:
public function __construct(YourClass $logger) { ... }
Circular Dependency
Circular dependency detected: ClassA -> ClassB -> ClassC -> ClassA
This happens when classes depend on each other in a loop.
Consider using setter injection or the factory pattern to break the cycle.
Unresolvable Scalar
Unable to resolve parameter of type 'string' in 'UserService'.
Scalar types (string, int, float, bool, array) cannot be auto-resolved.
Provide a default value for the parameter:
public function __construct(string $param = 'default') { ... }
Service Not Found (with suggestions)
No concrete class was found that implements:
"App\LogerInterface"
Did you forget to bind this interface to a concrete class?
Did you mean one of these?
- App\LoggerInterface
- App\Service\LoggerInterface
You might find some help here: https://gacela-project.com/docs/bootstrap/#bindings
Requirements
- PHP >= 8.1
- PSR-11 Container Interface
Testing
composer test # Run tests composer quality # Run static analysis composer test-coverage # Generate coverage report
Real-World Example
See how it's used in the Gacela Framework
License
MIT License. See LICENSE file for details.
gacela-project/container 适用场景与选型建议
gacela-project/container 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 149k 次下载、GitHub Stars 达 11, 最近一次更新时间为 2023 年 04 月 24 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「container」 「php」 「resolver」 「gacela」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 gacela-project/container 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 gacela-project/container 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 gacela-project/container 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
Type transformers, normalizers and resolvers.
resolve openapi keywords
PhpPlaisio: Company Resolver
A fast and intuitive dependency injection container.
PhpPlaisio: Uni Company Resolver
PhpPlaisio: Language Resolver
统计信息
- 总下载量: 149k
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 11
- 点击次数: 47
- 依赖项目数: 2
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2023-04-24