{"id":36310695,"url":"https://github.com/gacela-project/container","last_synced_at":"2026-01-31T02:20:13.165Z","repository":{"id":152428891,"uuid":"625903633","full_name":"gacela-project/container","owner":"gacela-project","description":"A minimalistic container dependency.","archived":false,"fork":false,"pushed_at":"2025-11-08T22:43:20.000Z","size":167,"stargazers_count":10,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-01-11T14:44:01.460Z","etag":null,"topics":["container","dependency-resolver","gacela","php","resolver"],"latest_commit_sha":null,"homepage":"https://packagist.org/packages/gacela-project/container","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gacela-project.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":".github/CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"custom":["https://chemaclass.com/sponsor"]}},"created_at":"2023-04-10T11:14:51.000Z","updated_at":"2026-01-09T13:20:37.000Z","dependencies_parsed_at":"2023-12-21T19:15:57.428Z","dependency_job_id":"deddb465-35c4-4f87-9413-6ab24c8792f7","html_url":"https://github.com/gacela-project/container","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/gacela-project/container","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gacela-project%2Fcontainer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gacela-project%2Fcontainer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gacela-project%2Fcontainer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gacela-project%2Fcontainer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gacela-project","download_url":"https://codeload.github.com/gacela-project/container/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gacela-project%2Fcontainer/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28926711,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-30T22:32:35.345Z","status":"online","status_checked_at":"2026-01-31T02:00:09.179Z","response_time":128,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["container","dependency-resolver","gacela","php","resolver"],"created_at":"2026-01-11T10:55:43.929Z","updated_at":"2026-01-31T02:20:13.154Z","avatar_url":"https://github.com/gacela-project.png","language":"PHP","funding_links":["https://chemaclass.com/sponsor"],"categories":[],"sub_categories":[],"readme":"# Gacela Container\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/gacela-project/container/actions\"\u003e\n    \u003cimg src=\"https://github.com/gacela-project/container/workflows/CI/badge.svg\" alt=\"GitHub Build Status\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://scrutinizer-ci.com/g/gacela-project/container/?branch=main\"\u003e\n    \u003cimg src=\"https://scrutinizer-ci.com/g/gacela-project/container/badges/quality-score.png?b=main\" alt=\"Scrutinizer Code Quality\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://scrutinizer-ci.com/g/gacela-project/container/?branch=main\"\u003e\n    \u003cimg src=\"https://scrutinizer-ci.com/g/gacela-project/container/badges/coverage.png?b=main\" alt=\"Scrutinizer Code Coverage\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://shepherd.dev/github/gacela-project/container\"\u003e\n    \u003cimg src=\"https://shepherd.dev/github/gacela-project/container/coverage.svg\" alt=\"Psalm Type-coverage Status\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/gacela-project/container/blob/master/LICENSE\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/License-MIT-green.svg\" alt=\"MIT Software License\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\nA minimalistic, PSR-11 compliant dependency injection container with automatic constructor injection and zero configuration.\n\n## Features\n\n- 🚀 **Zero Configuration**: Automatic constructor injection without verbose setup\n- 🔄 **Circular Dependency Detection**: Clear error messages when dependencies form a loop\n- 📦 **PSR-11 Compliant**: Standard container interface for interoperability\n- ⚡ **Performance Optimized**: Built-in caching and warmup capabilities\n- 🔍 **Introspection**: Debug and inspect container state easily\n- 🎯 **Type Safe**: Requires type hints for reliable dependency resolution\n- 🏷️ **PHP 8 Attributes**: Declarative configuration with #[Inject], #[Singleton], and #[Factory]\n\n## Installation\n\n```bash\ncomposer require gacela-project/container\n```\n\n## Quick Start\n\n### Basic Usage\n\n```php\nuse Gacela\\Container\\Container;\n\n// Simple auto-wiring\n$container = new Container();\n$instance = $container-\u003eget(YourClass::class);\n```\n\n### With Bindings\n\nMap interfaces to concrete implementations:\n\n```php\n$bindings = [\n    LoggerInterface::class =\u003e FileLogger::class,\n    CacheInterface::class =\u003e new RedisCache('localhost'),\n    ConfigInterface::class =\u003e fn() =\u003e loadConfig(),\n];\n\n$container = new Container($bindings);\n$logger = $container-\u003eget(LoggerInterface::class); // Returns FileLogger\n```\n\n### Contextual Bindings\n\nDifferent implementations based on which class needs them:\n\n```php\n// UserController gets FileLogger, AdminController gets DatabaseLogger\n$container-\u003ewhen(UserController::class)\n    -\u003eneeds(LoggerInterface::class)\n    -\u003egive(FileLogger::class);\n\n$container-\u003ewhen(AdminController::class)\n    -\u003eneeds(LoggerInterface::class)\n    -\u003egive(DatabaseLogger::class);\n\n// Multiple classes can share the same contextual binding\n$container-\u003ewhen([ServiceA::class, ServiceB::class])\n    -\u003eneeds(CacheInterface::class)\n    -\u003egive(RedisCache::class);\n```\n\n### PHP 8 Attributes\n\nUse attributes for declarative dependency configuration:\n\n#### #[Inject] - Specify Implementation\n\nOverride type hints to inject specific implementations:\n\n```php\nuse Gacela\\Container\\Attribute\\Inject;\n\nclass NotificationService {\n    public function __construct(\n        #[Inject(EmailLogger::class)]\n        private LoggerInterface $logger,\n    ) {}\n}\n\n// EmailLogger will be injected even if LoggerInterface is bound to FileLogger\n$service = $container-\u003eget(NotificationService::class);\n```\n\n#### #[Singleton] - Single Instance\n\nMark a class to be instantiated only once:\n\n```php\nuse Gacela\\Container\\Attribute\\Singleton;\n\n#[Singleton]\nclass DatabaseConnection {\n    public function __construct(private string $dsn) {}\n}\n\n$conn1 = $container-\u003eget(DatabaseConnection::class);\n$conn2 = $container-\u003eget(DatabaseConnection::class);\n// $conn1 === $conn2 (same instance)\n```\n\n#### #[Factory] - New Instances\n\nAlways create fresh instances:\n\n```php\nuse Gacela\\Container\\Attribute\\Factory;\n\n#[Factory]\nclass RequestContext {\n    public function __construct(private LoggerInterface $logger) {}\n}\n\n$ctx1 = $container-\u003eget(RequestContext::class);\n$ctx2 = $container-\u003eget(RequestContext::class);\n// $ctx1 !== $ctx2 (different instances)\n```\n\n**Performance Note:** Attribute checks are cached internally, so repeated instantiations of the same class avoid expensive reflection operations, providing 15-20% performance improvement.\n\n## How It Works\n\nThe container automatically resolves dependencies based on type hints:\n\n- **Primitive types**: Uses default values (must be provided)\n- **Classes**: Instantiates and resolves dependencies recursively\n- **Interfaces**: Resolves using bindings defined in the container\n\n### Example\n\n```php\nclass UserService {\n    public function __construct(\n        private UserRepository $repository,\n        private LoggerInterface $logger,\n    ) {}\n}\n\nclass UserRepository {\n    public function __construct(private PDO $pdo) {}\n}\n\n// Setup\n$bindings = [\n    LoggerInterface::class =\u003e FileLogger::class,\n    PDO::class =\u003e new PDO('mysql:host=localhost;dbname=app', 'user', 'pass'),\n];\n\n$container = new Container($bindings);\n\n// Auto-resolves UserService -\u003e UserRepository -\u003e PDO\n$service = $container-\u003eget(UserService::class);\n```\n\n## Advanced Features\n\n### Factory Services\n\nCreate new instances on every call:\n\n```php\n$factory = $container-\u003efactory(fn() =\u003e new TempFile());\n$container-\u003eset('temp_file', $factory);\n\n$file1 = $container-\u003eget('temp_file'); // New instance\n$file2 = $container-\u003eget('temp_file'); // Different instance\n```\n\n### Extending Services\n\nWrap or modify services (even before they're created):\n\n```php\n$container-\u003eset('logger', fn() =\u003e new FileLogger('/var/log/app.log'));\n\n$container-\u003eextend('logger', function ($logger, $container) {\n    return new LoggerDecorator($logger);\n});\n```\n\n### Protecting Closures\n\nPrevent closures from being executed:\n\n```php\n$closure = fn() =\u003e 'Hello World';\n$container-\u003eset('greeting', $container-\u003eprotect($closure));\n\n$result = $container-\u003eget('greeting'); // Returns the closure itself\n```\n\n### Resolving Callables\n\nAutomatically inject dependencies into any callable:\n\n```php\n$result = $container-\u003eresolve(function (LoggerInterface $logger, CacheInterface $cache) {\n    $logger-\u003einfo('Cache cleared');\n    return $cache-\u003eclear();\n});\n```\n\n### Service Introspection\n\nDebug and inspect container state:\n\n```php\n// Get all registered service IDs\n$services = $container-\u003egetRegisteredServices();\n\n// Check if service is a factory\nif ($container-\u003eisFactory('temp_file')) {\n    // Returns new instance each time\n}\n\n// Check if service is frozen (accessed)\nif ($container-\u003eisFrozen('logger')) {\n    // Cannot be modified anymore\n}\n\n// Get all bindings\n$bindings = $container-\u003egetBindings();\n\n// Get comprehensive statistics\n$stats = $container-\u003egetStats();\n/*\n[\n    'registered_services' =\u003e 42,\n    'frozen_services' =\u003e 15,\n    'factory_services' =\u003e 3,\n    'bindings' =\u003e 8,\n    'cached_dependencies' =\u003e 25,\n    'memory_usage' =\u003e '2.34 MB'\n]\n*/\n```\n\n### Performance Optimization\n\nPre-resolve dependencies for faster runtime:\n\n```php\n// During application bootstrap\n$container-\u003ewarmUp([\n    UserService::class,\n    OrderService::class,\n    PaymentProcessor::class,\n]);\n\n// Later requests benefit from cached dependency resolution\n$service = $container-\u003eget(UserService::class); // Faster!\n```\n\n### Service Aliasing\n\nCreate multiple names for the same service:\n\n```php\n// Create an alias\n$container-\u003ealias('db', PDO::class);\n\n// Access via alias or original name\n$db1 = $container-\u003eget('db');        // Same instance\n$db2 = $container-\u003eget(PDO::class);  // Same instance\n\n// Alias resolution is cached for optimal performance\n```\n\n## API Reference\n\n### Container Methods\n\n| Method | Description |\n|--------|-------------|\n| `get(string $id): mixed` | Retrieve or create a service |\n| `has(string $id): bool` | Check if service exists |\n| `set(string $id, mixed $instance): void` | Register a service |\n| `remove(string $id): void` | Remove a service |\n| `resolve(callable $callable): mixed` | Execute callable with dependency injection |\n| `factory(Closure $instance): Closure` | Mark service as factory (new instance each time) |\n| `extend(string $id, Closure $instance): Closure` | Wrap/modify a service |\n| `protect(Closure $instance): Closure` | Prevent closure execution |\n| `getRegisteredServices(): array` | Get all service IDs |\n| `isFactory(string $id): bool` | Check if service is a factory |\n| `isFrozen(string $id): bool` | Check if service is frozen |\n| `getBindings(): array` | Get all bindings |\n| `warmUp(array $classNames): void` | Pre-resolve dependencies |\n| `alias(string $alias, string $id): void` | Create an alias for a service (with caching) |\n| `getStats(): array` | Get container statistics for debugging and performance monitoring |\n| `when(string\\|array $concrete): ContextualBindingBuilder` | Define contextual bindings for specific classes |\n\n### Static Methods\n\n```php\n// Quick instantiation without container setup\n$instance = Container::create(YourClass::class);\n```\n\n## Best Practices\n\n### 1. Use Constructor Injection\n\n```php\n// Good\nclass UserController {\n    public function __construct(\n        private UserService $userService,\n        private LoggerInterface $logger\n    ) {}\n}\n\n// Avoid setter injection (not supported)\n```\n\n### 2. Always Use Type Hints\n\n```php\n// Good - type hint required\npublic function __construct(LoggerInterface $logger) {}\n\n// Bad - will throw exception\npublic function __construct($logger) {}\n```\n\n### 3. Provide Default Values for Scalars\n\n```php\n// Good\npublic function __construct(\n    UserRepository $repo,\n    int $maxRetries = 3,\n    string $env = 'production'\n) {}\n\n// Bad - scalars without defaults cannot be resolved\npublic function __construct(string $apiKey) {} // Exception!\n```\n\n### 4. Use Bindings for Interfaces\n\n```php\n// Always bind interfaces to implementations\n$bindings = [\n    LoggerInterface::class =\u003e FileLogger::class,\n    CacheInterface::class =\u003e RedisCache::class,\n];\n```\n\n### 5. Warm Up in Production\n\n```php\n// In your bootstrap file\n$container-\u003ewarmUp([\n    // List frequently used services\n    UserService::class,\n    AuthService::class,\n    Router::class,\n]);\n```\n\n## Error Handling\n\nThe container provides clear, actionable error messages with helpful suggestions:\n\n### Missing Type Hint\n```\nNo type hint found for parameter '$logger'.\nType hints are required for dependency injection to work properly.\n\nAdd a type hint to the parameter, for example:\n  public function __construct(YourClass $logger) { ... }\n```\n\n### Circular Dependency\n```\nCircular dependency detected: ClassA -\u003e ClassB -\u003e ClassC -\u003e ClassA\n\nThis happens when classes depend on each other in a loop.\nConsider using setter injection or the factory pattern to break the cycle.\n```\n\n### Unresolvable Scalar\n```\nUnable to resolve parameter of type 'string' in 'UserService'.\nScalar types (string, int, float, bool, array) cannot be auto-resolved.\n\nProvide a default value for the parameter:\n  public function __construct(string $param = 'default') { ... }\n```\n\n### Service Not Found (with suggestions)\n```\nNo concrete class was found that implements:\n\"App\\LogerInterface\"\nDid you forget to bind this interface to a concrete class?\n\nDid you mean one of these?\n  - App\\LoggerInterface\n  - App\\Service\\LoggerInterface\n\nYou might find some help here: https://gacela-project.com/docs/bootstrap/#bindings\n```\n\n## Requirements\n\n- PHP \u003e= 8.1\n- PSR-11 Container Interface\n\n## Testing\n\n```bash\ncomposer test          # Run tests\ncomposer quality       # Run static analysis\ncomposer test-coverage # Generate coverage report\n```\n\n## Real-World Example\n\nSee how it's used in the [Gacela Framework](https://github.com/gacela-project/gacela/blob/main/src/Framework/ClassResolver/AbstractClassResolver.php#L142)\n\n## License\n\nMIT License. See [LICENSE](LICENSE) file for details.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgacela-project%2Fcontainer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgacela-project%2Fcontainer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgacela-project%2Fcontainer/lists"}