Repository files navigation

Project logo: image showing an onion with the text Technically Cascade Contianer

Technically Cascade Container

πŸ§… Technically\CascadeContainer is simple yet powerful PSR-11 based service container implementation with layers and dependencies auto-wiring.

Test

Philosophy

Features

  • Inherits services from a parent PSR-11 Service Container
  • Can be forked into a new isolated container, inheriting all services definitions from the original container
  • PSR Container compatibility
  • Autowiring β€” automatic dependencies resolution
  • Full PHP 8.0+ features support for auto-wiring (e.g. union types)
useTechnically\CascadeContainer\CascadeContainer;
$container = newCascadeContainer();
$container->set('config', $config);
// Lazy-evaluated services$container->deferred('mailer', function () {
// lazily initialize mailer service here$mailer = /* ... */;
return$mailer;
});
// On-demand object factories (executes every time 'request' is obtained from the container)$container->factory('request', fn () => $requestFactory->createRequest());
// ✨ CASCADING LAYERS ✨// Fork the container into an isolated layer, inheriting everything from above.// Override services or define new ones. The changes won't affect the parent $container instance.$environment = $container->cascade();
// For example, we want to use a different mailer implementation in the tests environment:$environment->deferred('mailer', fn () => newNullMailer());

Usage

Installation

Use composer.

composer require technically/cascade-container

Basics

Checking presence, getting and setting service instances to the service container.

  • ::get(string $id): mixed β€” Get a service from the container by its name
  • ::has(string $id): bool β€” Check if there is a service defined in the container with the given name
  • ::set(string $id, mixed $instance): void β€” Define a service instance with the given name to the container
<?phpuseTechnically\CascadeContainer\CascadeContainer;
$container = newCascadeContainer();
// Set a service instance to the container$container->set('acme', newAcmeService());
// Check if there is a service binding for the given service echo$container->has('acme') ? 'ACME service is defined' : 'Nope';
// Get a service from container$acme = $container->get('acme');
$acme->orderProducts();

Using abstract interfaces

It's handy to bind services by their abstract interfaces to explicitly declare its interface on both definition and consumer sides.

<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */// Definition:// Note we bind an instance by its **abstract** interface.// This way you force consumers to not care about implementation details, but rely on the interface. $container->set(\Psr\Log\LoggerInterface::class, $myLogger);
// Consumer:// Then you have a consumer that needs a logger implementation,// but doesn't care on details. It can use any PSR-compatible logger.$logger = $container->get(\Psr\Log\LoggerInterface::class);
assert($loggerinstanceof \Psr\Log\LoggerInterface);
$logger->info('Nice!');

Aliases

Sometimes you may also want to bind the same service by different IDs. You can use aliases for that:

  • ::alias(string $serviceId, string $alias): void β€” Allow accessing an existing service by its new alias name
<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */$container->set(\Psr\Log\LoggerInterface::class, $myLogger);
$container->alias(\Psr\Log\LoggerInterface::class, alias: 'logger');
$logger = $container->get(\Psr\Log\LoggerInterface::class);
// ... or $logger = $container->get('logger'); // 100% equivalent$logger->info('Nice!');

Deferred resolvers

You can declare a service by providing a deferred resolver function for it. The service container will call that function for the first time the service is requested and remember the result.

This pattern is often called lazy initialization.

  • ::deferred(string $serviceId, callable $resolver): void β€” Provide a deferred resolver for the given service name.

Note: the callback function parameters are auto-wired the same way as with the ->call() API.

<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */$container->deferred('connection', function (ConnectionManager$manager) {
return$manager->initializeConnection();
});
// Consumer:$connection = $container->get('connection'); // The connection object$same_connection = $container->get('connection'); // The same connection objectassert($connection === $same_connection); // The same instance

Factories

You can also provide a factory function to be used to construct a new service instance every time it is requested.

It works very similarly to ->deferred(), but calls the factory function every time.

  • ::factory(string $serviceId, callable $factory): void β€” Bind a service to a factory function to be called every time it is requested.

Note: the callback function parameters are auto-wired the same way as with the ->call() API.

<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */// Definition:$container->factory('request', function (RequestFactory$factory) {
return$factory->createRequest();
});
// Consumer:$request = $container->get('request');
$another = $container->get('request');
assert($request !== $another); // Different instances

Extending a service

Sometimes it is necessary to extend/decorate an existing service by changing it or wrapping it into a decorator.

  • ::extend(string $serviceId, callable $extension): void β€” Extend an existing service by providing a transformation function.

    • Whatever the callback function returns will replace the previous instance.
    • If the service being extended is defined via a deferred resolver, the extension will become a deferred resolver too.
    • If the service being extended is defined as a factory, the extension will become a factory too.
<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */// Definition:$container->deferred('cache', function () {
returnnewRedisCache('127.0.0.1');
}); // Wrap the caching service with a logging decorator$container->extend('cache', function(RedisCache$cache, LoggerInterface$logger) { returnnewLoggingCacheDecorator($cache, $logger);
});
// Consumer:$cache = $container->get('cache'); // LoggingCacheDecorator object// Uses cache seamlessly as before (implying that RedisCache and LoggingCacheDecorator have the same interface)

Isolated layers forked from the service container

Sometimes it is necessary to create an isolated instance of the service container, inheriting its configured services and allowing to define more, without affecting the parent container.

Think of it as JavaScript variables scopes: a nested scope inherits all the variables from the parent scope. But defining new scope variables won't modify the parent scope. That's it.

  • ::cascade(): CascadeContainer β€” Create a new instance of the service container, inheriting all its defined services.
$project = newCascadeContainer();
$project->set('configuration', $config);
$environment = $project->cascade(); // MAGIC! ✨// Override existing services. It does not affect 'configuration' service in the parent container.$environment->set('configuration', $moduleConfig); // Define new services. They'll only exist on the current layer.$environment->factory('request', function () {
// ...
});
// and so onassert($project->get('configuration') !== $environment->get('configuration')); // Parent service "configuration" instance remained unchanged

Auto-wiring dependencies

Construct a class instance

You can construct any class instance automatically injecting class-hinted dependencies from the service container. It will try to resolve dependencies from the container or construct them resolving their dependencies recursively.

  • ::construct(string $className, array $parameters = []): mixed β€” Create a new instance of the given class auto-wiring its dependencies from the service container.
<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */// Class we need to inject dependencies intoclass LoggingCacheDecorator {
publicfunction__construct(CacheInterface$cache, LoggerInterface$logger, array$options = []) {
// initialize
}
}
$container->set(LoggerInterface::class, $logger);
$container->set(CacheInterface::class, $cache);
// Consumer:$cache = $container->construct(LoggingCacheDecorator::class);
// you can also provide constructor arguments in the second parameter:$cache = $container->construct(LoggingCacheDecorator::class, ['options' => ['level' => 'debug']]);

Calling a method

You can call any callable auto-wiring its dependencies from the service container.

  • ::call(callable $callable, array $parameters = []): mixed β€” Call the given callable auto-wiring its dependencies from the service container.
<?php/** @var $container RockSymphony\ServiceContainer\ServiceContainer */class MyController {
publicfunctionshowPost(string$url, PostsRepository$posts, TemplateEngine$templates)
{
$post = $posts->findByUrl($url);
return$templates->render('post.html', ['post' => $post]); }
}
$container->call([newMyController(), 'showPost'], ['url' => '/hello-world']);

You can as well pass a Closure to it:

$container->call(function (PostsRepository$repository) {
$repository->erase();
});

License

This project is licensed under the terms of the MIT license.

Credits

Implemented by πŸ‘Ύ Ivan Voskoboinyk.

About

πŸ§… A simple yet powerful PSR-11 service container with layers and dependencies auto-wiring

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

Project logo: image showing an onion with the text Technically Cascade Contianer

Technically Cascade Container

πŸ§… Technically\CascadeContainer is simple yet powerful PSR-11 based service container implementation with layers and dependencies auto-wiring.

Test

Philosophy

Features

  • Inherits services from a parent PSR-11 Service Container
  • Can be forked into a new isolated container, inheriting all services definitions from the original container
  • PSR Container compatibility
  • Autowiring β€” automatic dependencies resolution
  • Full PHP 8.0+ features support for auto-wiring (e.g. union types)
useTechnically\CascadeContainer\CascadeContainer;
$container = newCascadeContainer();
$container->set('config', $config);
// Lazy-evaluated services$container->deferred('mailer', function () {
// lazily initialize mailer service here$mailer = /* ... */;
return$mailer;
});
// On-demand object factories (executes every time 'request' is obtained from the container)$container->factory('request', fn () => $requestFactory->createRequest());
// ✨ CASCADING LAYERS ✨// Fork the container into an isolated layer, inheriting everything from above.// Override services or define new ones. The changes won't affect the parent $container instance.$environment = $container->cascade();
// For example, we want to use a different mailer implementation in the tests environment:$environment->deferred('mailer', fn () => newNullMailer());

Usage

Installation

Use composer.

composer require technically/cascade-container

Basics

Checking presence, getting and setting service instances to the service container.

  • ::get(string $id): mixed β€” Get a service from the container by its name
  • ::has(string $id): bool β€” Check if there is a service defined in the container with the given name
  • ::set(string $id, mixed $instance): void β€” Define a service instance with the given name to the container
<?phpuseTechnically\CascadeContainer\CascadeContainer;
$container = newCascadeContainer();
// Set a service instance to the container$container->set('acme', newAcmeService());
// Check if there is a service binding for the given service echo$container->has('acme') ? 'ACME service is defined' : 'Nope';
// Get a service from container$acme = $container->get('acme');
$acme->orderProducts();

Using abstract interfaces

It's handy to bind services by their abstract interfaces to explicitly declare its interface on both definition and consumer sides.

<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */// Definition:// Note we bind an instance by its **abstract** interface.// This way you force consumers to not care about implementation details, but rely on the interface. $container->set(\Psr\Log\LoggerInterface::class, $myLogger);
// Consumer:// Then you have a consumer that needs a logger implementation,// but doesn't care on details. It can use any PSR-compatible logger.$logger = $container->get(\Psr\Log\LoggerInterface::class);
assert($loggerinstanceof \Psr\Log\LoggerInterface);
$logger->info('Nice!');

Aliases

Sometimes you may also want to bind the same service by different IDs. You can use aliases for that:

  • ::alias(string $serviceId, string $alias): void β€” Allow accessing an existing service by its new alias name
<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */$container->set(\Psr\Log\LoggerInterface::class, $myLogger);
$container->alias(\Psr\Log\LoggerInterface::class, alias: 'logger');
$logger = $container->get(\Psr\Log\LoggerInterface::class);
// ... or $logger = $container->get('logger'); // 100% equivalent$logger->info('Nice!');

Deferred resolvers

You can declare a service by providing a deferred resolver function for it. The service container will call that function for the first time the service is requested and remember the result.

This pattern is often called lazy initialization.

  • ::deferred(string $serviceId, callable $resolver): void β€” Provide a deferred resolver for the given service name.

Note: the callback function parameters are auto-wired the same way as with the ->call() API.

<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */$container->deferred('connection', function (ConnectionManager$manager) {
return$manager->initializeConnection();
});
// Consumer:$connection = $container->get('connection'); // The connection object$same_connection = $container->get('connection'); // The same connection objectassert($connection === $same_connection); // The same instance

Factories

You can also provide a factory function to be used to construct a new service instance every time it is requested.

It works very similarly to ->deferred(), but calls the factory function every time.

  • ::factory(string $serviceId, callable $factory): void β€” Bind a service to a factory function to be called every time it is requested.

Note: the callback function parameters are auto-wired the same way as with the ->call() API.

<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */// Definition:$container->factory('request', function (RequestFactory$factory) {
return$factory->createRequest();
});
// Consumer:$request = $container->get('request');
$another = $container->get('request');
assert($request !== $another); // Different instances

Extending a service

Sometimes it is necessary to extend/decorate an existing service by changing it or wrapping it into a decorator.

  • ::extend(string $serviceId, callable $extension): void β€” Extend an existing service by providing a transformation function.

    • Whatever the callback function returns will replace the previous instance.
    • If the service being extended is defined via a deferred resolver, the extension will become a deferred resolver too.
    • If the service being extended is defined as a factory, the extension will become a factory too.
<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */// Definition:$container->deferred('cache', function () {
returnnewRedisCache('127.0.0.1');
}); // Wrap the caching service with a logging decorator$container->extend('cache', function(RedisCache$cache, LoggerInterface$logger) { returnnewLoggingCacheDecorator($cache, $logger);
});
// Consumer:$cache = $container->get('cache'); // LoggingCacheDecorator object// Uses cache seamlessly as before (implying that RedisCache and LoggingCacheDecorator have the same interface)

Isolated layers forked from the service container

Sometimes it is necessary to create an isolated instance of the service container, inheriting its configured services and allowing to define more, without affecting the parent container.

Think of it as JavaScript variables scopes: a nested scope inherits all the variables from the parent scope. But defining new scope variables won't modify the parent scope. That's it.

  • ::cascade(): CascadeContainer β€” Create a new instance of the service container, inheriting all its defined services.
$project = newCascadeContainer();
$project->set('configuration', $config);
$environment = $project->cascade(); // MAGIC! ✨// Override existing services. It does not affect 'configuration' service in the parent container.$environment->set('configuration', $moduleConfig); // Define new services. They'll only exist on the current layer.$environment->factory('request', function () {
// ...
});
// and so onassert($project->get('configuration') !== $environment->get('configuration')); // Parent service "configuration" instance remained unchanged

Auto-wiring dependencies

Construct a class instance

You can construct any class instance automatically injecting class-hinted dependencies from the service container. It will try to resolve dependencies from the container or construct them resolving their dependencies recursively.

  • ::construct(string $className, array $parameters = []): mixed β€” Create a new instance of the given class auto-wiring its dependencies from the service container.
<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */// Class we need to inject dependencies intoclass LoggingCacheDecorator {
publicfunction__construct(CacheInterface$cache, LoggerInterface$logger, array$options = []) {
// initialize
}
}
$container->set(LoggerInterface::class, $logger);
$container->set(CacheInterface::class, $cache);
// Consumer:$cache = $container->construct(LoggingCacheDecorator::class);
// you can also provide constructor arguments in the second parameter:$cache = $container->construct(LoggingCacheDecorator::class, ['options' => ['level' => 'debug']]);

Calling a method

You can call any callable auto-wiring its dependencies from the service container.

  • ::call(callable $callable, array $parameters = []): mixed β€” Call the given callable auto-wiring its dependencies from the service container.
<?php/** @var $container RockSymphony\ServiceContainer\ServiceContainer */class MyController {
publicfunctionshowPost(string$url, PostsRepository$posts, TemplateEngine$templates)
{
$post = $posts->findByUrl($url);
return$templates->render('post.html', ['post' => $post]); }
}
$container->call([newMyController(), 'showPost'], ['url' => '/hello-world']);

You can as well pass a Closure to it:

$container->call(function (PostsRepository$repository) {
$repository->erase();
});

License

This project is licensed under the terms of the MIT license.

Credits

Implemented by πŸ‘Ύ Ivan Voskoboinyk.

About

πŸ§… A simple yet powerful PSR-11 service container with layers and dependencies auto-wiring

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Project logo: image showing an onion with the text Technically Cascade Contianer

Technically Cascade Container

πŸ§… Technically\CascadeContainer is simple yet powerful PSR-11 based service container implementation with layers and dependencies auto-wiring.

Test

Philosophy

Features

  • Inherits services from a parent PSR-11 Service Container
  • Can be forked into a new isolated container, inheriting all services definitions from the original container
  • PSR Container compatibility
  • Autowiring β€” automatic dependencies resolution
  • Full PHP 8.0+ features support for auto-wiring (e.g. union types)
useTechnically\CascadeContainer\CascadeContainer;
$container = newCascadeContainer();
$container->set('config', $config);
// Lazy-evaluated services$container->deferred('mailer', function () {
// lazily initialize mailer service here$mailer = /* ... */;
return$mailer;
});
// On-demand object factories (executes every time 'request' is obtained from the container)$container->factory('request', fn () => $requestFactory->createRequest());
// ✨ CASCADING LAYERS ✨// Fork the container into an isolated layer, inheriting everything from above.// Override services or define new ones. The changes won't affect the parent $container instance.$environment = $container->cascade();
// For example, we want to use a different mailer implementation in the tests environment:$environment->deferred('mailer', fn () => newNullMailer());

Usage

Installation

Use composer.

composer require technically/cascade-container

Basics

Checking presence, getting and setting service instances to the service container.

  • ::get(string $id): mixed β€” Get a service from the container by its name
  • ::has(string $id): bool β€” Check if there is a service defined in the container with the given name
  • ::set(string $id, mixed $instance): void β€” Define a service instance with the given name to the container
<?phpuseTechnically\CascadeContainer\CascadeContainer;
$container = newCascadeContainer();
// Set a service instance to the container$container->set('acme', newAcmeService());
// Check if there is a service binding for the given service echo$container->has('acme') ? 'ACME service is defined' : 'Nope';
// Get a service from container$acme = $container->get('acme');
$acme->orderProducts();

Using abstract interfaces

It's handy to bind services by their abstract interfaces to explicitly declare its interface on both definition and consumer sides.

<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */// Definition:// Note we bind an instance by its **abstract** interface.// This way you force consumers to not care about implementation details, but rely on the interface. $container->set(\Psr\Log\LoggerInterface::class, $myLogger);
// Consumer:// Then you have a consumer that needs a logger implementation,// but doesn't care on details. It can use any PSR-compatible logger.$logger = $container->get(\Psr\Log\LoggerInterface::class);
assert($loggerinstanceof \Psr\Log\LoggerInterface);
$logger->info('Nice!');

Aliases

Sometimes you may also want to bind the same service by different IDs. You can use aliases for that:

  • ::alias(string $serviceId, string $alias): void β€” Allow accessing an existing service by its new alias name
<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */$container->set(\Psr\Log\LoggerInterface::class, $myLogger);
$container->alias(\Psr\Log\LoggerInterface::class, alias: 'logger');
$logger = $container->get(\Psr\Log\LoggerInterface::class);
// ... or $logger = $container->get('logger'); // 100% equivalent$logger->info('Nice!');

Deferred resolvers

You can declare a service by providing a deferred resolver function for it. The service container will call that function for the first time the service is requested and remember the result.

This pattern is often called lazy initialization.

  • ::deferred(string $serviceId, callable $resolver): void β€” Provide a deferred resolver for the given service name.

Note: the callback function parameters are auto-wired the same way as with the ->call() API.

<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */$container->deferred('connection', function (ConnectionManager$manager) {
return$manager->initializeConnection();
});
// Consumer:$connection = $container->get('connection'); // The connection object$same_connection = $container->get('connection'); // The same connection objectassert($connection === $same_connection); // The same instance

Factories

You can also provide a factory function to be used to construct a new service instance every time it is requested.

It works very similarly to ->deferred(), but calls the factory function every time.

  • ::factory(string $serviceId, callable $factory): void β€” Bind a service to a factory function to be called every time it is requested.

Note: the callback function parameters are auto-wired the same way as with the ->call() API.

<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */// Definition:$container->factory('request', function (RequestFactory$factory) {
return$factory->createRequest();
});
// Consumer:$request = $container->get('request');
$another = $container->get('request');
assert($request !== $another); // Different instances

Extending a service

Sometimes it is necessary to extend/decorate an existing service by changing it or wrapping it into a decorator.

  • ::extend(string $serviceId, callable $extension): void β€” Extend an existing service by providing a transformation function.

    • Whatever the callback function returns will replace the previous instance.
    • If the service being extended is defined via a deferred resolver, the extension will become a deferred resolver too.
    • If the service being extended is defined as a factory, the extension will become a factory too.
<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */// Definition:$container->deferred('cache', function () {
returnnewRedisCache('127.0.0.1');
}); // Wrap the caching service with a logging decorator$container->extend('cache', function(RedisCache$cache, LoggerInterface$logger) { returnnewLoggingCacheDecorator($cache, $logger);
});
// Consumer:$cache = $container->get('cache'); // LoggingCacheDecorator object// Uses cache seamlessly as before (implying that RedisCache and LoggingCacheDecorator have the same interface)

Isolated layers forked from the service container

Sometimes it is necessary to create an isolated instance of the service container, inheriting its configured services and allowing to define more, without affecting the parent container.

Think of it as JavaScript variables scopes: a nested scope inherits all the variables from the parent scope. But defining new scope variables won't modify the parent scope. That's it.

  • ::cascade(): CascadeContainer β€” Create a new instance of the service container, inheriting all its defined services.
$project = newCascadeContainer();
$project->set('configuration', $config);
$environment = $project->cascade(); // MAGIC! ✨// Override existing services. It does not affect 'configuration' service in the parent container.$environment->set('configuration', $moduleConfig); // Define new services. They'll only exist on the current layer.$environment->factory('request', function () {
// ...
});
// and so onassert($project->get('configuration') !== $environment->get('configuration')); // Parent service "configuration" instance remained unchanged

Auto-wiring dependencies

Construct a class instance

You can construct any class instance automatically injecting class-hinted dependencies from the service container. It will try to resolve dependencies from the container or construct them resolving their dependencies recursively.

  • ::construct(string $className, array $parameters = []): mixed β€” Create a new instance of the given class auto-wiring its dependencies from the service container.
<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */// Class we need to inject dependencies intoclass LoggingCacheDecorator {
publicfunction__construct(CacheInterface$cache, LoggerInterface$logger, array$options = []) {
// initialize
}
}
$container->set(LoggerInterface::class, $logger);
$container->set(CacheInterface::class, $cache);
// Consumer:$cache = $container->construct(LoggingCacheDecorator::class);
// you can also provide constructor arguments in the second parameter:$cache = $container->construct(LoggingCacheDecorator::class, ['options' => ['level' => 'debug']]);

Calling a method

You can call any callable auto-wiring its dependencies from the service container.

  • ::call(callable $callable, array $parameters = []): mixed β€” Call the given callable auto-wiring its dependencies from the service container.
<?php/** @var $container RockSymphony\ServiceContainer\ServiceContainer */class MyController {
publicfunctionshowPost(string$url, PostsRepository$posts, TemplateEngine$templates)
{
$post = $posts->findByUrl($url);
return$templates->render('post.html', ['post' => $post]); }
}
$container->call([newMyController(), 'showPost'], ['url' => '/hello-world']);

You can as well pass a Closure to it:

$container->call(function (PostsRepository$repository) {
$repository->erase();
});

License

This project is licensed under the terms of the MIT license.

Credits

Implemented by πŸ‘Ύ Ivan Voskoboinyk.

About

πŸ§… A simple yet powerful PSR-11 service container with layers and dependencies auto-wiring

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Project logo: image showing an onion with the text Technically Cascade Contianer

Technically Cascade Container

πŸ§… Technically\CascadeContainer is simple yet powerful PSR-11 based service container implementation with layers and dependencies auto-wiring.

Test

Philosophy

Features

  • Inherits services from a parent PSR-11 Service Container
  • Can be forked into a new isolated container, inheriting all services definitions from the original container
  • PSR Container compatibility
  • Autowiring β€” automatic dependencies resolution
  • Full PHP 8.0+ features support for auto-wiring (e.g. union types)
useTechnically\CascadeContainer\CascadeContainer;
$container = newCascadeContainer();
$container->set('config', $config);
// Lazy-evaluated services$container->deferred('mailer', function () {
// lazily initialize mailer service here$mailer = /* ... */;
return$mailer;
});
// On-demand object factories (executes every time 'request' is obtained from the container)$container->factory('request', fn () => $requestFactory->createRequest());
// ✨ CASCADING LAYERS ✨// Fork the container into an isolated layer, inheriting everything from above.// Override services or define new ones. The changes won't affect the parent $container instance.$environment = $container->cascade();
// For example, we want to use a different mailer implementation in the tests environment:$environment->deferred('mailer', fn () => newNullMailer());

Usage

Installation

Use composer.

composer require technically/cascade-container

Basics

Checking presence, getting and setting service instances to the service container.

  • ::get(string $id): mixed β€” Get a service from the container by its name
  • ::has(string $id): bool β€” Check if there is a service defined in the container with the given name
  • ::set(string $id, mixed $instance): void β€” Define a service instance with the given name to the container
<?phpuseTechnically\CascadeContainer\CascadeContainer;
$container = newCascadeContainer();
// Set a service instance to the container$container->set('acme', newAcmeService());
// Check if there is a service binding for the given service echo$container->has('acme') ? 'ACME service is defined' : 'Nope';
// Get a service from container$acme = $container->get('acme');
$acme->orderProducts();

Using abstract interfaces

It's handy to bind services by their abstract interfaces to explicitly declare its interface on both definition and consumer sides.

<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */// Definition:// Note we bind an instance by its **abstract** interface.// This way you force consumers to not care about implementation details, but rely on the interface. $container->set(\Psr\Log\LoggerInterface::class, $myLogger);
// Consumer:// Then you have a consumer that needs a logger implementation,// but doesn't care on details. It can use any PSR-compatible logger.$logger = $container->get(\Psr\Log\LoggerInterface::class);
assert($loggerinstanceof \Psr\Log\LoggerInterface);
$logger->info('Nice!');

Aliases

Sometimes you may also want to bind the same service by different IDs. You can use aliases for that:

  • ::alias(string $serviceId, string $alias): void β€” Allow accessing an existing service by its new alias name
<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */$container->set(\Psr\Log\LoggerInterface::class, $myLogger);
$container->alias(\Psr\Log\LoggerInterface::class, alias: 'logger');
$logger = $container->get(\Psr\Log\LoggerInterface::class);
// ... or $logger = $container->get('logger'); // 100% equivalent$logger->info('Nice!');

Deferred resolvers

You can declare a service by providing a deferred resolver function for it. The service container will call that function for the first time the service is requested and remember the result.

This pattern is often called lazy initialization.

  • ::deferred(string $serviceId, callable $resolver): void β€” Provide a deferred resolver for the given service name.

Note: the callback function parameters are auto-wired the same way as with the ->call() API.

<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */$container->deferred('connection', function (ConnectionManager$manager) {
return$manager->initializeConnection();
});
// Consumer:$connection = $container->get('connection'); // The connection object$same_connection = $container->get('connection'); // The same connection objectassert($connection === $same_connection); // The same instance

Factories

You can also provide a factory function to be used to construct a new service instance every time it is requested.

It works very similarly to ->deferred(), but calls the factory function every time.

  • ::factory(string $serviceId, callable $factory): void β€” Bind a service to a factory function to be called every time it is requested.

Note: the callback function parameters are auto-wired the same way as with the ->call() API.

<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */// Definition:$container->factory('request', function (RequestFactory$factory) {
return$factory->createRequest();
});
// Consumer:$request = $container->get('request');
$another = $container->get('request');
assert($request !== $another); // Different instances

Extending a service

Sometimes it is necessary to extend/decorate an existing service by changing it or wrapping it into a decorator.

  • ::extend(string $serviceId, callable $extension): void β€” Extend an existing service by providing a transformation function.

    • Whatever the callback function returns will replace the previous instance.
    • If the service being extended is defined via a deferred resolver, the extension will become a deferred resolver too.
    • If the service being extended is defined as a factory, the extension will become a factory too.
<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */// Definition:$container->deferred('cache', function () {
returnnewRedisCache('127.0.0.1');
}); // Wrap the caching service with a logging decorator$container->extend('cache', function(RedisCache$cache, LoggerInterface$logger) { returnnewLoggingCacheDecorator($cache, $logger);
});
// Consumer:$cache = $container->get('cache'); // LoggingCacheDecorator object// Uses cache seamlessly as before (implying that RedisCache and LoggingCacheDecorator have the same interface)

Isolated layers forked from the service container

Sometimes it is necessary to create an isolated instance of the service container, inheriting its configured services and allowing to define more, without affecting the parent container.

Think of it as JavaScript variables scopes: a nested scope inherits all the variables from the parent scope. But defining new scope variables won't modify the parent scope. That's it.

  • ::cascade(): CascadeContainer β€” Create a new instance of the service container, inheriting all its defined services.
$project = newCascadeContainer();
$project->set('configuration', $config);
$environment = $project->cascade(); // MAGIC! ✨// Override existing services. It does not affect 'configuration' service in the parent container.$environment->set('configuration', $moduleConfig); // Define new services. They'll only exist on the current layer.$environment->factory('request', function () {
// ...
});
// and so onassert($project->get('configuration') !== $environment->get('configuration')); // Parent service "configuration" instance remained unchanged

Auto-wiring dependencies

Construct a class instance

You can construct any class instance automatically injecting class-hinted dependencies from the service container. It will try to resolve dependencies from the container or construct them resolving their dependencies recursively.

  • ::construct(string $className, array $parameters = []): mixed β€” Create a new instance of the given class auto-wiring its dependencies from the service container.
<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */// Class we need to inject dependencies intoclass LoggingCacheDecorator {
publicfunction__construct(CacheInterface$cache, LoggerInterface$logger, array$options = []) {
// initialize
}
}
$container->set(LoggerInterface::class, $logger);
$container->set(CacheInterface::class, $cache);
// Consumer:$cache = $container->construct(LoggingCacheDecorator::class);
// you can also provide constructor arguments in the second parameter:$cache = $container->construct(LoggingCacheDecorator::class, ['options' => ['level' => 'debug']]);

Calling a method

You can call any callable auto-wiring its dependencies from the service container.

  • ::call(callable $callable, array $parameters = []): mixed β€” Call the given callable auto-wiring its dependencies from the service container.
<?php/** @var $container RockSymphony\ServiceContainer\ServiceContainer */class MyController {
publicfunctionshowPost(string$url, PostsRepository$posts, TemplateEngine$templates)
{
$post = $posts->findByUrl($url);
return$templates->render('post.html', ['post' => $post]); }
}
$container->call([newMyController(), 'showPost'], ['url' => '/hello-world']);

You can as well pass a Closure to it:

$container->call(function (PostsRepository$repository) {
$repository->erase();
});

License

This project is licensed under the terms of the MIT license.

Credits

Implemented by πŸ‘Ύ Ivan Voskoboinyk.

About

πŸ§… A simple yet powerful PSR-11 service container with layers and dependencies auto-wiring

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

Project logo: image showing an onion with the text Technically Cascade Contianer

Technically Cascade Container

πŸ§… Technically\CascadeContainer is simple yet powerful PSR-11 based service container implementation with layers and dependencies auto-wiring.

Test

Philosophy

Features

  • Inherits services from a parent PSR-11 Service Container
  • Can be forked into a new isolated container, inheriting all services definitions from the original container
  • PSR Container compatibility
  • Autowiring β€” automatic dependencies resolution
  • Full PHP 8.0+ features support for auto-wiring (e.g. union types)
useTechnically\CascadeContainer\CascadeContainer;
$container = newCascadeContainer();
$container->set('config', $config);
// Lazy-evaluated services$container->deferred('mailer', function () {
// lazily initialize mailer service here$mailer = /* ... */;
return$mailer;
});
// On-demand object factories (executes every time 'request' is obtained from the container)$container->factory('request', fn () => $requestFactory->createRequest());
// ✨ CASCADING LAYERS ✨// Fork the container into an isolated layer, inheriting everything from above.// Override services or define new ones. The changes won't affect the parent $container instance.$environment = $container->cascade();
// For example, we want to use a different mailer implementation in the tests environment:$environment->deferred('mailer', fn () => newNullMailer());

Usage

Installation

Use composer.

composer require technically/cascade-container

Basics

Checking presence, getting and setting service instances to the service container.

  • ::get(string $id): mixed β€” Get a service from the container by its name
  • ::has(string $id): bool β€” Check if there is a service defined in the container with the given name
  • ::set(string $id, mixed $instance): void β€” Define a service instance with the given name to the container
<?phpuseTechnically\CascadeContainer\CascadeContainer;
$container = newCascadeContainer();
// Set a service instance to the container$container->set('acme', newAcmeService());
// Check if there is a service binding for the given service echo$container->has('acme') ? 'ACME service is defined' : 'Nope';
// Get a service from container$acme = $container->get('acme');
$acme->orderProducts();

Using abstract interfaces

It's handy to bind services by their abstract interfaces to explicitly declare its interface on both definition and consumer sides.

<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */// Definition:// Note we bind an instance by its **abstract** interface.// This way you force consumers to not care about implementation details, but rely on the interface. $container->set(\Psr\Log\LoggerInterface::class, $myLogger);
// Consumer:// Then you have a consumer that needs a logger implementation,// but doesn't care on details. It can use any PSR-compatible logger.$logger = $container->get(\Psr\Log\LoggerInterface::class);
assert($loggerinstanceof \Psr\Log\LoggerInterface);
$logger->info('Nice!');

Aliases

Sometimes you may also want to bind the same service by different IDs. You can use aliases for that:

  • ::alias(string $serviceId, string $alias): void β€” Allow accessing an existing service by its new alias name
<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */$container->set(\Psr\Log\LoggerInterface::class, $myLogger);
$container->alias(\Psr\Log\LoggerInterface::class, alias: 'logger');
$logger = $container->get(\Psr\Log\LoggerInterface::class);
// ... or $logger = $container->get('logger'); // 100% equivalent$logger->info('Nice!');

Deferred resolvers

You can declare a service by providing a deferred resolver function for it. The service container will call that function for the first time the service is requested and remember the result.

This pattern is often called lazy initialization.

  • ::deferred(string $serviceId, callable $resolver): void β€” Provide a deferred resolver for the given service name.

Note: the callback function parameters are auto-wired the same way as with the ->call() API.

<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */$container->deferred('connection', function (ConnectionManager$manager) {
return$manager->initializeConnection();
});
// Consumer:$connection = $container->get('connection'); // The connection object$same_connection = $container->get('connection'); // The same connection objectassert($connection === $same_connection); // The same instance

Factories

You can also provide a factory function to be used to construct a new service instance every time it is requested.

It works very similarly to ->deferred(), but calls the factory function every time.

  • ::factory(string $serviceId, callable $factory): void β€” Bind a service to a factory function to be called every time it is requested.

Note: the callback function parameters are auto-wired the same way as with the ->call() API.

<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */// Definition:$container->factory('request', function (RequestFactory$factory) {
return$factory->createRequest();
});
// Consumer:$request = $container->get('request');
$another = $container->get('request');
assert($request !== $another); // Different instances

Extending a service

Sometimes it is necessary to extend/decorate an existing service by changing it or wrapping it into a decorator.

  • ::extend(string $serviceId, callable $extension): void β€” Extend an existing service by providing a transformation function.

    • Whatever the callback function returns will replace the previous instance.
    • If the service being extended is defined via a deferred resolver, the extension will become a deferred resolver too.
    • If the service being extended is defined as a factory, the extension will become a factory too.
<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */// Definition:$container->deferred('cache', function () {
returnnewRedisCache('127.0.0.1');
}); // Wrap the caching service with a logging decorator$container->extend('cache', function(RedisCache$cache, LoggerInterface$logger) { returnnewLoggingCacheDecorator($cache, $logger);
});
// Consumer:$cache = $container->get('cache'); // LoggingCacheDecorator object// Uses cache seamlessly as before (implying that RedisCache and LoggingCacheDecorator have the same interface)

Isolated layers forked from the service container

Sometimes it is necessary to create an isolated instance of the service container, inheriting its configured services and allowing to define more, without affecting the parent container.

Think of it as JavaScript variables scopes: a nested scope inherits all the variables from the parent scope. But defining new scope variables won't modify the parent scope. That's it.

  • ::cascade(): CascadeContainer β€” Create a new instance of the service container, inheriting all its defined services.
$project = newCascadeContainer();
$project->set('configuration', $config);
$environment = $project->cascade(); // MAGIC! ✨// Override existing services. It does not affect 'configuration' service in the parent container.$environment->set('configuration', $moduleConfig); // Define new services. They'll only exist on the current layer.$environment->factory('request', function () {
// ...
});
// and so onassert($project->get('configuration') !== $environment->get('configuration')); // Parent service "configuration" instance remained unchanged

Auto-wiring dependencies

Construct a class instance

You can construct any class instance automatically injecting class-hinted dependencies from the service container. It will try to resolve dependencies from the container or construct them resolving their dependencies recursively.

  • ::construct(string $className, array $parameters = []): mixed β€” Create a new instance of the given class auto-wiring its dependencies from the service container.
<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */// Class we need to inject dependencies intoclass LoggingCacheDecorator {
publicfunction__construct(CacheInterface$cache, LoggerInterface$logger, array$options = []) {
// initialize
}
}
$container->set(LoggerInterface::class, $logger);
$container->set(CacheInterface::class, $cache);
// Consumer:$cache = $container->construct(LoggingCacheDecorator::class);
// you can also provide constructor arguments in the second parameter:$cache = $container->construct(LoggingCacheDecorator::class, ['options' => ['level' => 'debug']]);

Calling a method

You can call any callable auto-wiring its dependencies from the service container.

  • ::call(callable $callable, array $parameters = []): mixed β€” Call the given callable auto-wiring its dependencies from the service container.
<?php/** @var $container RockSymphony\ServiceContainer\ServiceContainer */class MyController {
publicfunctionshowPost(string$url, PostsRepository$posts, TemplateEngine$templates)
{
$post = $posts->findByUrl($url);
return$templates->render('post.html', ['post' => $post]); }
}
$container->call([newMyController(), 'showPost'], ['url' => '/hello-world']);

You can as well pass a Closure to it:

$container->call(function (PostsRepository$repository) {
$repository->erase();
});

License

This project is licensed under the terms of the MIT license.

Credits

Implemented by πŸ‘Ύ Ivan Voskoboinyk.

About

πŸ§… A simple yet powerful PSR-11 service container with layers and dependencies auto-wiring

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Project logo: image showing an onion with the text Technically Cascade Contianer

Technically Cascade Container

πŸ§… Technically\CascadeContainer is simple yet powerful PSR-11 based service container implementation with layers and dependencies auto-wiring.

Test

Philosophy

Features

  • Inherits services from a parent PSR-11 Service Container
  • Can be forked into a new isolated container, inheriting all services definitions from the original container
  • PSR Container compatibility
  • Autowiring β€” automatic dependencies resolution
  • Full PHP 8.0+ features support for auto-wiring (e.g. union types)
useTechnically\CascadeContainer\CascadeContainer;
$container = newCascadeContainer();
$container->set('config', $config);
// Lazy-evaluated services$container->deferred('mailer', function () {
// lazily initialize mailer service here$mailer = /* ... */;
return$mailer;
});
// On-demand object factories (executes every time 'request' is obtained from the container)$container->factory('request', fn () => $requestFactory->createRequest());
// ✨ CASCADING LAYERS ✨// Fork the container into an isolated layer, inheriting everything from above.// Override services or define new ones. The changes won't affect the parent $container instance.$environment = $container->cascade();
// For example, we want to use a different mailer implementation in the tests environment:$environment->deferred('mailer', fn () => newNullMailer());

Usage

Installation

Use composer.

composer require technically/cascade-container

Basics

Checking presence, getting and setting service instances to the service container.

  • ::get(string $id): mixed β€” Get a service from the container by its name
  • ::has(string $id): bool β€” Check if there is a service defined in the container with the given name
  • ::set(string $id, mixed $instance): void β€” Define a service instance with the given name to the container
<?phpuseTechnically\CascadeContainer\CascadeContainer;
$container = newCascadeContainer();
// Set a service instance to the container$container->set('acme', newAcmeService());
// Check if there is a service binding for the given service echo$container->has('acme') ? 'ACME service is defined' : 'Nope';
// Get a service from container$acme = $container->get('acme');
$acme->orderProducts();

Using abstract interfaces

It's handy to bind services by their abstract interfaces to explicitly declare its interface on both definition and consumer sides.

<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */// Definition:// Note we bind an instance by its **abstract** interface.// This way you force consumers to not care about implementation details, but rely on the interface. $container->set(\Psr\Log\LoggerInterface::class, $myLogger);
// Consumer:// Then you have a consumer that needs a logger implementation,// but doesn't care on details. It can use any PSR-compatible logger.$logger = $container->get(\Psr\Log\LoggerInterface::class);
assert($loggerinstanceof \Psr\Log\LoggerInterface);
$logger->info('Nice!');

Aliases

Sometimes you may also want to bind the same service by different IDs. You can use aliases for that:

  • ::alias(string $serviceId, string $alias): void β€” Allow accessing an existing service by its new alias name
<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */$container->set(\Psr\Log\LoggerInterface::class, $myLogger);
$container->alias(\Psr\Log\LoggerInterface::class, alias: 'logger');
$logger = $container->get(\Psr\Log\LoggerInterface::class);
// ... or $logger = $container->get('logger'); // 100% equivalent$logger->info('Nice!');

Deferred resolvers

You can declare a service by providing a deferred resolver function for it. The service container will call that function for the first time the service is requested and remember the result.

This pattern is often called lazy initialization.

  • ::deferred(string $serviceId, callable $resolver): void β€” Provide a deferred resolver for the given service name.

Note: the callback function parameters are auto-wired the same way as with the ->call() API.

<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */$container->deferred('connection', function (ConnectionManager$manager) {
return$manager->initializeConnection();
});
// Consumer:$connection = $container->get('connection'); // The connection object$same_connection = $container->get('connection'); // The same connection objectassert($connection === $same_connection); // The same instance

Factories

You can also provide a factory function to be used to construct a new service instance every time it is requested.

It works very similarly to ->deferred(), but calls the factory function every time.

  • ::factory(string $serviceId, callable $factory): void β€” Bind a service to a factory function to be called every time it is requested.

Note: the callback function parameters are auto-wired the same way as with the ->call() API.

<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */// Definition:$container->factory('request', function (RequestFactory$factory) {
return$factory->createRequest();
});
// Consumer:$request = $container->get('request');
$another = $container->get('request');
assert($request !== $another); // Different instances

Extending a service

Sometimes it is necessary to extend/decorate an existing service by changing it or wrapping it into a decorator.

  • ::extend(string $serviceId, callable $extension): void β€” Extend an existing service by providing a transformation function.

    • Whatever the callback function returns will replace the previous instance.
    • If the service being extended is defined via a deferred resolver, the extension will become a deferred resolver too.
    • If the service being extended is defined as a factory, the extension will become a factory too.
<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */// Definition:$container->deferred('cache', function () {
returnnewRedisCache('127.0.0.1');
}); // Wrap the caching service with a logging decorator$container->extend('cache', function(RedisCache$cache, LoggerInterface$logger) { returnnewLoggingCacheDecorator($cache, $logger);
});
// Consumer:$cache = $container->get('cache'); // LoggingCacheDecorator object// Uses cache seamlessly as before (implying that RedisCache and LoggingCacheDecorator have the same interface)

Isolated layers forked from the service container

Sometimes it is necessary to create an isolated instance of the service container, inheriting its configured services and allowing to define more, without affecting the parent container.

Think of it as JavaScript variables scopes: a nested scope inherits all the variables from the parent scope. But defining new scope variables won't modify the parent scope. That's it.

  • ::cascade(): CascadeContainer β€” Create a new instance of the service container, inheriting all its defined services.
$project = newCascadeContainer();
$project->set('configuration', $config);
$environment = $project->cascade(); // MAGIC! ✨// Override existing services. It does not affect 'configuration' service in the parent container.$environment->set('configuration', $moduleConfig); // Define new services. They'll only exist on the current layer.$environment->factory('request', function () {
// ...
});
// and so onassert($project->get('configuration') !== $environment->get('configuration')); // Parent service "configuration" instance remained unchanged

Auto-wiring dependencies

Construct a class instance

You can construct any class instance automatically injecting class-hinted dependencies from the service container. It will try to resolve dependencies from the container or construct them resolving their dependencies recursively.

  • ::construct(string $className, array $parameters = []): mixed β€” Create a new instance of the given class auto-wiring its dependencies from the service container.
<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */// Class we need to inject dependencies intoclass LoggingCacheDecorator {
publicfunction__construct(CacheInterface$cache, LoggerInterface$logger, array$options = []) {
// initialize
}
}
$container->set(LoggerInterface::class, $logger);
$container->set(CacheInterface::class, $cache);
// Consumer:$cache = $container->construct(LoggingCacheDecorator::class);
// you can also provide constructor arguments in the second parameter:$cache = $container->construct(LoggingCacheDecorator::class, ['options' => ['level' => 'debug']]);

Calling a method

You can call any callable auto-wiring its dependencies from the service container.

  • ::call(callable $callable, array $parameters = []): mixed β€” Call the given callable auto-wiring its dependencies from the service container.
<?php/** @var $container RockSymphony\ServiceContainer\ServiceContainer */class MyController {
publicfunctionshowPost(string$url, PostsRepository$posts, TemplateEngine$templates)
{
$post = $posts->findByUrl($url);
return$templates->render('post.html', ['post' => $post]); }
}
$container->call([newMyController(), 'showPost'], ['url' => '/hello-world']);

You can as well pass a Closure to it:

$container->call(function (PostsRepository$repository) {
$repository->erase();
});

License

This project is licensed under the terms of the MIT license.

Credits

Implemented by πŸ‘Ύ Ivan Voskoboinyk.

About

πŸ§… A simple yet powerful PSR-11 service container with layers and dependencies auto-wiring

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Project logo: image showing an onion with the text Technically Cascade Contianer

Technically Cascade Container

πŸ§… Technically\CascadeContainer is simple yet powerful PSR-11 based service container implementation with layers and dependencies auto-wiring.

Test

Philosophy

Features

  • Inherits services from a parent PSR-11 Service Container
  • Can be forked into a new isolated container, inheriting all services definitions from the original container
  • PSR Container compatibility
  • Autowiring β€” automatic dependencies resolution
  • Full PHP 8.0+ features support for auto-wiring (e.g. union types)
useTechnically\CascadeContainer\CascadeContainer;
$container = newCascadeContainer();
$container->set('config', $config);
// Lazy-evaluated services$container->deferred('mailer', function () {
// lazily initialize mailer service here$mailer = /* ... */;
return$mailer;
});
// On-demand object factories (executes every time 'request' is obtained from the container)$container->factory('request', fn () => $requestFactory->createRequest());
// ✨ CASCADING LAYERS ✨// Fork the container into an isolated layer, inheriting everything from above.// Override services or define new ones. The changes won't affect the parent $container instance.$environment = $container->cascade();
// For example, we want to use a different mailer implementation in the tests environment:$environment->deferred('mailer', fn () => newNullMailer());

Usage

Installation

Use composer.

composer require technically/cascade-container

Basics

Checking presence, getting and setting service instances to the service container.

  • ::get(string $id): mixed β€” Get a service from the container by its name
  • ::has(string $id): bool β€” Check if there is a service defined in the container with the given name
  • ::set(string $id, mixed $instance): void β€” Define a service instance with the given name to the container
<?phpuseTechnically\CascadeContainer\CascadeContainer;
$container = newCascadeContainer();
// Set a service instance to the container$container->set('acme', newAcmeService());
// Check if there is a service binding for the given service echo$container->has('acme') ? 'ACME service is defined' : 'Nope';
// Get a service from container$acme = $container->get('acme');
$acme->orderProducts();

Using abstract interfaces

It's handy to bind services by their abstract interfaces to explicitly declare its interface on both definition and consumer sides.

<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */// Definition:// Note we bind an instance by its **abstract** interface.// This way you force consumers to not care about implementation details, but rely on the interface. $container->set(\Psr\Log\LoggerInterface::class, $myLogger);
// Consumer:// Then you have a consumer that needs a logger implementation,// but doesn't care on details. It can use any PSR-compatible logger.$logger = $container->get(\Psr\Log\LoggerInterface::class);
assert($loggerinstanceof \Psr\Log\LoggerInterface);
$logger->info('Nice!');

Aliases

Sometimes you may also want to bind the same service by different IDs. You can use aliases for that:

  • ::alias(string $serviceId, string $alias): void β€” Allow accessing an existing service by its new alias name
<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */$container->set(\Psr\Log\LoggerInterface::class, $myLogger);
$container->alias(\Psr\Log\LoggerInterface::class, alias: 'logger');
$logger = $container->get(\Psr\Log\LoggerInterface::class);
// ... or $logger = $container->get('logger'); // 100% equivalent$logger->info('Nice!');

Deferred resolvers

You can declare a service by providing a deferred resolver function for it. The service container will call that function for the first time the service is requested and remember the result.

This pattern is often called lazy initialization.

  • ::deferred(string $serviceId, callable $resolver): void β€” Provide a deferred resolver for the given service name.

Note: the callback function parameters are auto-wired the same way as with the ->call() API.

<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */$container->deferred('connection', function (ConnectionManager$manager) {
return$manager->initializeConnection();
});
// Consumer:$connection = $container->get('connection'); // The connection object$same_connection = $container->get('connection'); // The same connection objectassert($connection === $same_connection); // The same instance

Factories

You can also provide a factory function to be used to construct a new service instance every time it is requested.

It works very similarly to ->deferred(), but calls the factory function every time.

  • ::factory(string $serviceId, callable $factory): void β€” Bind a service to a factory function to be called every time it is requested.

Note: the callback function parameters are auto-wired the same way as with the ->call() API.

<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */// Definition:$container->factory('request', function (RequestFactory$factory) {
return$factory->createRequest();
});
// Consumer:$request = $container->get('request');
$another = $container->get('request');
assert($request !== $another); // Different instances

Extending a service

Sometimes it is necessary to extend/decorate an existing service by changing it or wrapping it into a decorator.

  • ::extend(string $serviceId, callable $extension): void β€” Extend an existing service by providing a transformation function.

    • Whatever the callback function returns will replace the previous instance.
    • If the service being extended is defined via a deferred resolver, the extension will become a deferred resolver too.
    • If the service being extended is defined as a factory, the extension will become a factory too.
<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */// Definition:$container->deferred('cache', function () {
returnnewRedisCache('127.0.0.1');
}); // Wrap the caching service with a logging decorator$container->extend('cache', function(RedisCache$cache, LoggerInterface$logger) { returnnewLoggingCacheDecorator($cache, $logger);
});
// Consumer:$cache = $container->get('cache'); // LoggingCacheDecorator object// Uses cache seamlessly as before (implying that RedisCache and LoggingCacheDecorator have the same interface)

Isolated layers forked from the service container

Sometimes it is necessary to create an isolated instance of the service container, inheriting its configured services and allowing to define more, without affecting the parent container.

Think of it as JavaScript variables scopes: a nested scope inherits all the variables from the parent scope. But defining new scope variables won't modify the parent scope. That's it.

  • ::cascade(): CascadeContainer β€” Create a new instance of the service container, inheriting all its defined services.
$project = newCascadeContainer();
$project->set('configuration', $config);
$environment = $project->cascade(); // MAGIC! ✨// Override existing services. It does not affect 'configuration' service in the parent container.$environment->set('configuration', $moduleConfig); // Define new services. They'll only exist on the current layer.$environment->factory('request', function () {
// ...
});
// and so onassert($project->get('configuration') !== $environment->get('configuration')); // Parent service "configuration" instance remained unchanged

Auto-wiring dependencies

Construct a class instance

You can construct any class instance automatically injecting class-hinted dependencies from the service container. It will try to resolve dependencies from the container or construct them resolving their dependencies recursively.

  • ::construct(string $className, array $parameters = []): mixed β€” Create a new instance of the given class auto-wiring its dependencies from the service container.
<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */// Class we need to inject dependencies intoclass LoggingCacheDecorator {
publicfunction__construct(CacheInterface$cache, LoggerInterface$logger, array$options = []) {
// initialize
}
}
$container->set(LoggerInterface::class, $logger);
$container->set(CacheInterface::class, $cache);
// Consumer:$cache = $container->construct(LoggingCacheDecorator::class);
// you can also provide constructor arguments in the second parameter:$cache = $container->construct(LoggingCacheDecorator::class, ['options' => ['level' => 'debug']]);

Calling a method

You can call any callable auto-wiring its dependencies from the service container.

  • ::call(callable $callable, array $parameters = []): mixed β€” Call the given callable auto-wiring its dependencies from the service container.
<?php/** @var $container RockSymphony\ServiceContainer\ServiceContainer */class MyController {
publicfunctionshowPost(string$url, PostsRepository$posts, TemplateEngine$templates)
{
$post = $posts->findByUrl($url);
return$templates->render('post.html', ['post' => $post]); }
}
$container->call([newMyController(), 'showPost'], ['url' => '/hello-world']);

You can as well pass a Closure to it:

$container->call(function (PostsRepository$repository) {
$repository->erase();
});

License

This project is licensed under the terms of the MIT license.

Credits

Implemented by πŸ‘Ύ Ivan Voskoboinyk.

About

πŸ§… A simple yet powerful PSR-11 service container with layers and dependencies auto-wiring

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

Project logo: image showing an onion with the text Technically Cascade Contianer

Technically Cascade Container

πŸ§… Technically\CascadeContainer is simple yet powerful PSR-11 based service container implementation with layers and dependencies auto-wiring.

Test

Philosophy

Features

  • Inherits services from a parent PSR-11 Service Container
  • Can be forked into a new isolated container, inheriting all services definitions from the original container
  • PSR Container compatibility
  • Autowiring β€” automatic dependencies resolution
  • Full PHP 8.0+ features support for auto-wiring (e.g. union types)
useTechnically\CascadeContainer\CascadeContainer;
$container = newCascadeContainer();
$container->set('config', $config);
// Lazy-evaluated services$container->deferred('mailer', function () {
// lazily initialize mailer service here$mailer = /* ... */;
return$mailer;
});
// On-demand object factories (executes every time 'request' is obtained from the container)$container->factory('request', fn () => $requestFactory->createRequest());
// ✨ CASCADING LAYERS ✨// Fork the container into an isolated layer, inheriting everything from above.// Override services or define new ones. The changes won't affect the parent $container instance.$environment = $container->cascade();
// For example, we want to use a different mailer implementation in the tests environment:$environment->deferred('mailer', fn () => newNullMailer());

Usage

Installation

Use composer.

composer require technically/cascade-container

Basics

Checking presence, getting and setting service instances to the service container.

  • ::get(string $id): mixed β€” Get a service from the container by its name
  • ::has(string $id): bool β€” Check if there is a service defined in the container with the given name
  • ::set(string $id, mixed $instance): void β€” Define a service instance with the given name to the container
<?phpuseTechnically\CascadeContainer\CascadeContainer;
$container = newCascadeContainer();
// Set a service instance to the container$container->set('acme', newAcmeService());
// Check if there is a service binding for the given service echo$container->has('acme') ? 'ACME service is defined' : 'Nope';
// Get a service from container$acme = $container->get('acme');
$acme->orderProducts();

Using abstract interfaces

It's handy to bind services by their abstract interfaces to explicitly declare its interface on both definition and consumer sides.

<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */// Definition:// Note we bind an instance by its **abstract** interface.// This way you force consumers to not care about implementation details, but rely on the interface. $container->set(\Psr\Log\LoggerInterface::class, $myLogger);
// Consumer:// Then you have a consumer that needs a logger implementation,// but doesn't care on details. It can use any PSR-compatible logger.$logger = $container->get(\Psr\Log\LoggerInterface::class);
assert($loggerinstanceof \Psr\Log\LoggerInterface);
$logger->info('Nice!');

Aliases

Sometimes you may also want to bind the same service by different IDs. You can use aliases for that:

  • ::alias(string $serviceId, string $alias): void β€” Allow accessing an existing service by its new alias name
<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */$container->set(\Psr\Log\LoggerInterface::class, $myLogger);
$container->alias(\Psr\Log\LoggerInterface::class, alias: 'logger');
$logger = $container->get(\Psr\Log\LoggerInterface::class);
// ... or $logger = $container->get('logger'); // 100% equivalent$logger->info('Nice!');

Deferred resolvers

You can declare a service by providing a deferred resolver function for it. The service container will call that function for the first time the service is requested and remember the result.

This pattern is often called lazy initialization.

  • ::deferred(string $serviceId, callable $resolver): void β€” Provide a deferred resolver for the given service name.

Note: the callback function parameters are auto-wired the same way as with the ->call() API.

<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */$container->deferred('connection', function (ConnectionManager$manager) {
return$manager->initializeConnection();
});
// Consumer:$connection = $container->get('connection'); // The connection object$same_connection = $container->get('connection'); // The same connection objectassert($connection === $same_connection); // The same instance

Factories

You can also provide a factory function to be used to construct a new service instance every time it is requested.

It works very similarly to ->deferred(), but calls the factory function every time.

  • ::factory(string $serviceId, callable $factory): void β€” Bind a service to a factory function to be called every time it is requested.

Note: the callback function parameters are auto-wired the same way as with the ->call() API.

<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */// Definition:$container->factory('request', function (RequestFactory$factory) {
return$factory->createRequest();
});
// Consumer:$request = $container->get('request');
$another = $container->get('request');
assert($request !== $another); // Different instances

Extending a service

Sometimes it is necessary to extend/decorate an existing service by changing it or wrapping it into a decorator.

  • ::extend(string $serviceId, callable $extension): void β€” Extend an existing service by providing a transformation function.

    • Whatever the callback function returns will replace the previous instance.
    • If the service being extended is defined via a deferred resolver, the extension will become a deferred resolver too.
    • If the service being extended is defined as a factory, the extension will become a factory too.
<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */// Definition:$container->deferred('cache', function () {
returnnewRedisCache('127.0.0.1');
}); // Wrap the caching service with a logging decorator$container->extend('cache', function(RedisCache$cache, LoggerInterface$logger) { returnnewLoggingCacheDecorator($cache, $logger);
});
// Consumer:$cache = $container->get('cache'); // LoggingCacheDecorator object// Uses cache seamlessly as before (implying that RedisCache and LoggingCacheDecorator have the same interface)

Isolated layers forked from the service container

Sometimes it is necessary to create an isolated instance of the service container, inheriting its configured services and allowing to define more, without affecting the parent container.

Think of it as JavaScript variables scopes: a nested scope inherits all the variables from the parent scope. But defining new scope variables won't modify the parent scope. That's it.

  • ::cascade(): CascadeContainer β€” Create a new instance of the service container, inheriting all its defined services.
$project = newCascadeContainer();
$project->set('configuration', $config);
$environment = $project->cascade(); // MAGIC! ✨// Override existing services. It does not affect 'configuration' service in the parent container.$environment->set('configuration', $moduleConfig); // Define new services. They'll only exist on the current layer.$environment->factory('request', function () {
// ...
});
// and so onassert($project->get('configuration') !== $environment->get('configuration')); // Parent service "configuration" instance remained unchanged

Auto-wiring dependencies

Construct a class instance

You can construct any class instance automatically injecting class-hinted dependencies from the service container. It will try to resolve dependencies from the container or construct them resolving their dependencies recursively.

  • ::construct(string $className, array $parameters = []): mixed β€” Create a new instance of the given class auto-wiring its dependencies from the service container.
<?php/** @var $container \Technically\CascadeContainer\CascadeContainer */// Class we need to inject dependencies intoclass LoggingCacheDecorator {
publicfunction__construct(CacheInterface$cache, LoggerInterface$logger, array$options = []) {
// initialize
}
}
$container->set(LoggerInterface::class, $logger);
$container->set(CacheInterface::class, $cache);
// Consumer:$cache = $container->construct(LoggingCacheDecorator::class);
// you can also provide constructor arguments in the second parameter:$cache = $container->construct(LoggingCacheDecorator::class, ['options' => ['level' => 'debug']]);

Calling a method

You can call any callable auto-wiring its dependencies from the service container.

  • ::call(callable $callable, array $parameters = []): mixed β€” Call the given callable auto-wiring its dependencies from the service container.
<?php/** @var $container RockSymphony\ServiceContainer\ServiceContainer */class MyController {
publicfunctionshowPost(string$url, PostsRepository$posts, TemplateEngine$templates)
{
$post = $posts->findByUrl($url);
return$templates->render('post.html', ['post' => $post]); }
}
$container->call([newMyController(), 'showPost'], ['url' => '/hello-world']);

You can as well pass a Closure to it:

$container->call(function (PostsRepository$repository) {
$repository->erase();
});

License

This project is licensed under the terms of the MIT license.

Credits

Implemented by πŸ‘Ύ Ivan Voskoboinyk.

About

πŸ§… A simple yet powerful PSR-11 service container with layers and dependencies auto-wiring

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages