- What is open-solid/callable-invoker and what problem does it solve?
- It is a lightweight PHP utility that executes any callable while automatically resolving its parameters from a dependency injection container. Instead of writing manual reflection code or chaining app()->make() calls, you hand over a closure, invokable class, or [object, method] pair and let the invoker supply the arguments. This makes hooks, actions, and command-style handlers easy to wire up dynamically without hardcoding constructor dependencies.
- How do I install open-solid/callable-invoker with Composer?
- Install it with composer require open-solid/callable-invoker. Composer will pull the package and register its autoloading automatically. Always check the version constraints in the package's composer.json to confirm the PHP and Laravel versions your project needs are supported before committing to the upgrade.
- Does open-solid/callable-invoker require Laravel, or can I use it in plain PHP?
- It works in both environments. The package is framework-agnostic and can resolve parameters from any PSR-11 compatible container, so plain PHP projects with a container such as PHP-DI work fine. In Laravel it can lean on the framework's service container, which is what makes it convenient for hooks and handlers inside an app.
- What callable formats can I pass to the invoker?
- You can pass a closure or anonymous function, an invokable class instance (one that defines __invoke), or a two-element array in the classic [object, method] form. String function names and static callables generally work too since they are valid PHP callables. This flexibility means you can accept a callable from configuration, a database column, or a hook registry without forcing a single shape on callers.
- How does the automatic dependency resolution actually work?
- The invoker inspects the callable's signature via reflection, then asks the container for each required parameter by its type hint. Scalar parameters with default values fall back to those defaults rather than being resolved from the container. The result is that a handler can type-hint a service, a repository, or a value object and receive a fully built instance without any manual wiring.
- How is this different from Laravel's app()->call()?
- Laravel's app()->call() already performs container-backed method injection, so for many Laravel-only use cases the built-in helper is enough. The value of this package is that it is a small, standalone abstraction that behaves consistently across plain PHP and Laravel, and gives you a single explicit dependency for callable execution that is easy to swap, fake, or test. Teams building framework-agnostic packages often prefer this over depending on the Laravel container directly.
- Can I pass my own arguments in addition to the auto-resolved dependencies?
- The core behaviour is container-driven resolution, so the primary path is to let the invoker build parameters for you. If you need to inject runtime values, the common pattern is to bind them into the container or wrap the target callable in a closure that captures them. Check the README for the exact signature of the invoke method in the version you install, since argument-passing behaviour can vary between releases.
- Does it respect Laravel container bindings, singletons, and contextual bindings?
- Yes. Because parameters are resolved through the container rather than instantiated reflectively with new, anything registered with bind, singleton, scoped, or instance is honoured exactly as it would be anywhere else in your application. That also means interfaces resolve to whatever implementation you have bound, which is the main reason to use a container-aware invoker instead of raw reflection.
- How do I test code that uses the callable invoker?
- Because the invoker is just a small object with a clear method, you can type-hint it in your own classes and swap it for a fake or mock in unit tests. For integration tests, bind fakes and spies into the Laravel container and assert that the underlying callable received the expected dependencies and was invoked once. If a handler needs a specific service, bind a mock under the same interface in the test container so the resolution path matches production.
- What happens if a dependency cannot be resolved, and is the package still maintained?
- When a required type-hinted parameter is not bound and cannot be auto-wired, the underlying container throws a resolution exception rather than passing null, so failures surface early and loudly. Give optional dependencies default values or explicit bindings to avoid that. For maintenance status, check the repository's release history, open issues, and composer.json constraints, since PHP and Laravel support ranges are updated there over time.