How does reflection in Laravel actually work?
I tried to debug it to see how Laravel uses reflection in a controller's constructor or methods to resolve their dependencies and sub-dependencies and then and give it back to us.
But I found it hard, and it's very complicated to see and to even understand 50% of. Jumping from class to class, I can't really see it. I tried a few times by debugging it with low results of understanding.
I am very impressed by this and by reflection, and the way Laravel uses it makes my heart burn—it's just beautiful. And I wish to fully understand that—the whole process—in general, and step by step.
Beginning from hitting the route to finally having, let's say, dd($x), where $x is from a method argument and is a TestClass that has another dependency of TestClass2 that should be constructed through: $x = new TestClass(new TestClass2());
I think those are beautiful mechanics and architecture, and understanding this is something I want so badly.
So again, my question is: how does reflection in Laravel actually work?
It's not about dd guys... Let's say without dd. Just as I said earlier - when we have this object instantiated from the class method. It's not about dumping it, it's just about having it from method injection by reflection.
The dd was only an example. It can even be die(var_dump()); and it will work
Laravel uses PHP's reflection API for several components. Of these, the inverson-of-control (IoC) dependency injection container and controller method injection are most visible to developers.
To more clearly illustrate the use of reflection, here's a dramatically simplified version of the routine Laravel's IoC container class uses to build up an object's dependencies through constructor injection:
function build($className)
{
$reflector = new ReflectionClass($className);
$constructor = $reflector->getConstructor();
foreach ($constructor->getParameters() as $dependency) {
$instances[] = build($dependency->getClass()->name);
}
return $reflector->newInstanceArgs($instances);
}
As we can see, the concept isn't too difficult to understand. The container uses PHP's ReflectionClass to find the names of the classes in an object's constructor, and then loops through each of these names recursively to create instances of each object in the dependency tree. With these instances, build() finally instantiates the original class and passes the dependencies as arguments to the constructor.
Controller method injection uses the same container functionality shown above to resolve instances of dependencies declared as method parameters, but there's a bit of extra logic needed to separate class dependencies from route parameters:
function dispatch(Route $route, Controller $controller, $methodName)
{
$routeParameters = $route->parametersWithoutNulls();
$method = new ReflectionMethod($controller, $methodName);
foreach ($method->getParameters() as $index => $parameter) {
$class = $parameter->getClass();
if ($class !== null) {
$instance = build($class->name);
array_splice($routeParameters, $index, 0, [ $instance ]);
}
}
$controller->callAction($methodName, $routeParameters);
}
Again, this adaptation is slimmed-down to highlight the role reflection plays and relies on our build() function shown previously. The ControllerDispatcher class uses the getParameters() method of PHP's ReflectionMethod to determine which parameters a controller method expects, and then loops through these to find parameters that represent dependencies that it can resolve from the container. Then, it splices each dependency it finds back into the array of route parameters that it passes back to the controller method defined for the route. See RouteDependencyResolverTrait for details.
If we ignore the application bootstrapping process, this dependency injection cascade typically starts for a request when Laravel maps a request to a route, and then determines which controller to pass the request to. Laravel first resolves an instance of the controller from the container, which builds out any constructor-injected dependencies. Then, Laravel finds the appropriate controller method and resolves any more dependencies for the arguments as needed.
As shown here, Laravel uses relatively simple techniques to implement these tools using reflection. However, unlike the examples shown in this answer, the framework adds a lot of additional code to make them as robust and flexible as they are today.
Related
I'm looking at Laravel's service container docs, specifically the binding section.
What are the differences and when should I use each type of binding? The documentation mentions:
Simple Binding
Singleton Binding
Instance Binding
Primitive binding
Interface binding
First, let's see what it actually is:
IoC container is a component that knows how instances are created. It also knows of all their underlying dependencies and how to resolve them.
Container's knowledge about instance creation and dependency resolving might be guided by the programmer. That's why Laravel's container provides various binding APIs for me, and you.
"Resolving out of the container" is a phrase you read/hear a lot. It means that you tell the container to make something for you based on the [optional] guidance you gave her previously.
Before you continue reading about bindings, I strongly recommend you to read this answer:
What is Laravel IoC Container in simple words?
Simple Binding
app()->bind(DatabaseWriter::class, function ($app) {
return new DatabaseWriter(
$app->make(DatabaseAdapterInterface)
);
});
You say to the container that when you want to resolve an instance of DatabaseWriter class, follow this logic I just told you in the closure coz I know better. Every single time that you want to resolve the class, you must follow this and deliver me a new instance.
You use this type of bindings all the time. You're giving the container small recipes on how to make your things for you.
Singleton Binding
Same as simple bindings, with one obvious difference. You're telling the container that I want only one instance of this class across my whole application. The first time you're resolving the class, follow the logic in the closure I passed to you, but be sure that you just return that only instance every other time you want to resolve it. Deliver me the only instance you were allowed to make.
It's a singleton, right? Once a singleton binding is resolved, the same object instance will be returned on subsequent calls into the container.
Obviously, you use this type of binding when you want to utilize the Singleton pattern. It's rare these days.
Instance Binding
It's like doing a favor for the container. You don't tell her how to instantiate a certain class, you do it yourself and just give her the instance. She holds it for you and returns it on subsequent calls into the container.
It's especially handy when you're unit-testing. If you bind a mock instance to the container for some class, all the subsequent calls to app()->make() will return that mock for you. So you're practically injecting a class mock all over the app when the actual class is used.
class QuestionsControllerTest extends TestCase
{
public function testQuestionListing()
{
$questionMock = Mockery::mock('Question')
->shouldReceive('latest')
->once()
->getMock();
// You're telling the container that everytime anyone
// wants a Question instance, give them this mock I just
// gave you.
$this->app->instance('Question', $this->questionMock);
// whatever...
}
}
Primitive Binding
Laravel's container provides a DSL for you to tell her how to resolve primitives as well. You say when BillingController class wants a $taxRate variable and it's not passed, give it 0.2. It's like setting default values from far far away!
app()->when('App\Http\Controllers\BillingController')
->needs('$taxRate')
->give(.2);
The use-case might be rare, but you might need them occasionally. This example might be a lil bit more sensual:
app()->when('App\Http\Controllers\CustomerController')
->needs('$customers')
->give(function() {
return Customer::paying();
});
Interface Binding
It's used when you want to bind interfaces to concrete implementations.
After reading a dozen articles on SOLID and how to be a better programmer, you decide to follow Dependency Inversion principle and instead of depending on concrete instances, you depend on abstractions.
After all, it's a good practice, in or out of Laravel.
class DatabaseWriter {
protected $db;
// Any concrete implementation of this interface will do
// Now, that I depend on this DatabaseAdapterInterface contract,
// I can work with MySQL, MongoDB and WhatevaDB! Awesome!
public function __construct(DatabaseAdapterInterface $db)
{
$this->db = $db;
}
public function write()
{
$this->db->query('...');
}
}
Without Laravel's container, you first need to create a concrete implementation of DatabaseAdapterInterface and pass it through DatabaseWriter's constructor to be able to instantiate it:
$dbWriter = new DatabaseWriter(new MongodbAdapter)
If MongodbAdapter has its own dependencies, you might end up here:
// Looks familiar, right?
// These are those recipes you used to give to Laravel container
// through simple binding.
$dbWriter = new DatabaseWriter(new MongodbAdapter(new MongodbConnection))
But with Laravel's container in the party, you tell her that when anyone asks for a concrete implementation of DatabaseAdapterInterface, ask no more and give them a MongodbAdapter:
app()->bind(DatabaseAdapterInterface::class, MongodbAdapter::class)
Then you go on and resolve an instance of DatabaseWriter out of container, like a boss:
$dbWriter = app()->make(DatabaseWriter::class)
Much easier and cleaner, right? You remove all the obvious clutter and move it to somewhere else. Your AppServiceProvider maybe.
OK, let's see how she works in this scenario. First, she probes DatabaseWriter for possible dependencies (through reflection), sees that it needs a DatabaseAdapterInterface. Checks her notebook, recalls that you told her that MongodbAdapter is the concrete implementation of that interface. Makes one and hand it over to DatabaseWriter.
You use these type of bindings almost all the time, if you're adhering to dependency inversion principle.
OK, enough with the chatter, let's see how she really works:
https://github.com/laravel/framework/blob/5.3/src/Illuminate/Container/Container.php#L627
We have some legacy laravel projects which use facades in the classes.
use Cache;
LegacyClass
{
public function cacheFunctionOne()
{
$result = Cache::someFunction('parameter');
// logic to manipulate result
return $result;
}
public function cacheFunctionTwo()
{
$result = Cache::someFunction('parameter');
// different logic to manipulate result
return $result;
}
}
Our more recent projects use dependency injection of the underlying laravel classes that the facades represent as has been hinted at by Taylor Otwell himself. (We use constructor injection for each class, but to keep the example short, here I use method injection and use a single class.)
use Illuminate\Cache\Repository as Cache;
ModernClass
{
public function cacheFunctionOne(Cache $cache)
{
$result = $cache->someFunction('parameter');
// logic to manipulate result
return $result;
}
public function cacheFunctionTwo(Cache $cache)
{
$result = $cache->someFunction('parameter');
// different logic to manipulate result
return $result;
}
}
I know facades can be mocked
public function testExample()
{
Cache::shouldReceive('get')
->once()
->with('key')
->andReturn('value');
$this->visit('/users')->see('value');
}
Which works nicely for unit tests. The problem I am trying to understand is if these facades are mocked 'globally'.
For example, lets imagine I am writing an integration test (testing a few interconnected classes while mocking services - not an end to end test using live services) which at some point, executes two separate classes which contain the same facade that calls the same method with the same parameters.
In between these classes being called, is some complex functionality that changes what data is returned by that facades method using the same parameter.*
$modernClass->cacheFunctionOne($cache); // easily mocked
// logic that changes data returned by laravel Cache object function 'someFunction'
$modernClass->cacheFunctionTwo($cache); // easily mocked with a different mock
Our modern classes are easy to test because the underlying class that the facade represents is injected into each class (in this example, each method). This means I can create two separate mocks and inject them into each class (method) to mock the different results.
$legacyClass->cacheFunctionOne();
// logic that changes data returned by laravel Cache object function 'someFunction'
$legacyClass->cacheFunctionTwo();
In the legacy systems though, it would seem that the mocked facade is 'global' so that when the facade is run in each class, the exact same value is returned.
Am I correct in thinking this?
*I understand this example may seem completely redundant from a code architecture and testing point of view, but I am stripping out all real functionality to try and give some sort of 'simple' example of what I am asking.
Dependency Injection vs Facades
One of the major benefits of Dependency Injection is that code becomes a lot more testable once you start injecting dependencies into methods instead of instantiating/hardcoding them inside the method. This is because you can pass in the dependencies from inside unit tests and they will propagate through the code.
See: http://slashnode.com/dependency-injection/
Dependency Injection stands in stark contrast to Facades. Facades are static global classes, the PHP language does not allow one to overwrite or replace static functions on static classes. The Laravel facades use Mockery to provide mock functionality and they are limited by the same facts as above.
The issue for integration testing can come where you are hoping to retrieve data from a non-mocked Cache but once you use Facade::shouldReceive() then Facade::get() will be overridden by the mocked Cache. The reverse is also true. As a result, Facades are inappropriate where you are interleaving calls for mocked and unmocked data.
In order to test your code with the different data sets that you require, the best practice would be to refactor your legacy code to use DI.
Integration Tests
Easier method
An alternative is to call multiple Facade::shouldReceive() with expectations at the beginning of your integration test. Ensuring that you have the right numbers of expectations in the right order for each of the calls you will make in the integration test. This would probably be the faster way to write tests given your existing codebase.
Harder method
Whilst dependency injection is programming best practice. It could very well be that your codebase has so many legacy classes that it would take an unbelievable amount of time to refactor. In this case, it might be worthwhile considering end-to-end integration tests using a test database with fixtures.
Appendix:
For how mockery is called by Facade see - function createMockByName(): https://github.com/laravel/framework/blob/5.3/src/Illuminate/Support/Facades/Facade.php
For examples on chaining mockery calls see fhinkel commented on Feb 6, 2015: https://github.com/padraic/mockery/issues/401
I have read a number of sources that hint that laravel facade's ultimately exist for convenience and that these classes should instead be injected to allow loose coupling. Even Taylor Otwell has a post explaining how to do this. It seems I am not the only one to wonder this.
use Redirect;
class Example class
{
public function example()
{
return Redirect::route("route.name");
}
}
would become
use Illuminate\Routing\Redirector as Redirect;
class Example class
{
protected $redirect;
public function __constructor(Redirect $redirect)
{
$this->redirect = $redirect
}
public function example()
{
return $this->redirect->route("route.name");
}
}
This is fine except that I am starting to find that some constructors and methods are beginning to take four+ parameters.
Since the Laravel IoC seems to only inject into class constructors and certain methods (controllers), even when I have fairly lean functions and classes, I am finding that constructors of the classes are becoming packed out with the needed classes that then get injected into the needed methods.
Now I am finding that if I continue down this approach that I will need my own IoC container, which feels like reinventing the wheel if I am using a framework like laravel?
For example I use services to control the business / view logic rather than controllers dealing with them - they simply route the views. So a controller will first take its corresponding service, then then the parameter in its url. One service function also needs to check the values from a form, so then I need Request and Validator. Just like that, I have four parameters.
// MyServiceInterface is binded using the laravel container
use Interfaces\MyServiceInterface;
use Illuminate\Http\Request;
use Illuminate\Validation\Factory as Validator;
...
public function exampleController(MyServiceInterface $my_service, Request $request, Validator $validator, $user_id)
{
// Call some method in the service to do complex validation
$validation = $my_service->doValidation($request, $validator);
// Also return the view information
$viewinfo = $my_service->getViewInfo($user_id);
if ($validation === 'ok') {
return view("some_view", ['view_info'=>$viewinfo]);
} else {
return view("another_view", ['view_info'=>$viewinfo]);
}
}
This is a single example. In reality, many of my constructors already have multiple classes being injected (Models, Services, Parameters, Facades). I have started to 'offload' the constructor injection (when applicable) to method injection, and have the classes calling those methods use their constructors to inject dependencies instead.
I have been told that more than four parameters for a method or class constructor as a rule of thumb is bad practice / code smell. However I cannot see how you can really avoid this if you choose the path of injecting laravel facades.
Have I got this idea wrong? Are my classes / functions not lean enough? Am I missing the point of laravels container or do I really need to think of creating my own IoC container? Some others answers seems to hint at the laravel container being able to eliminate my issue?
That said, there doesn't seem to be a definitive consensus on the issue...
This is one of the benefits of constructor injection - it becomes obvious when you class is doing to much, because the constructor parameters grow too large.
1st thing to do is split up controllers that have too many responsibilities.
Say you have a page controller:
Class PageController
{
public function __construct(
Request $request,
ClientRepositoryInterface $clientrepo,
StaffRepositortInterface $staffRepo
)
{
$this->clientRepository = $clientRepo;
//etc etc
}
public function aboutAction()
{
$teamMembers = $this->staffRepository->getAll();
//render view
}
public function allClientsAction()
{
$clients = $this->clientRepository->getAll();
//render view
}
public function addClientAction(Request $request, Validator $validator)
{
$this->clientRepository->createFromArray($request->all() $validator);
//do stuff
}
}
This is a prime candidate for splitting into two controllers, ClientController and AboutController.
Once you have done that, if you still have too many* dependencies, its time to look for what i will call indirect dependancies (because i cant think of the proper name for them!) - dependencies that are not directly used by the dependant class, but instead passed on to another dependency.
An example of this is addClientAction - it requires a request and a validator, just to pass them to the clientRepostory.
We can re factor by creating a new class specifically for creating clients from requests, thus reducing our dependencies, and simplifying both the controller and the repository:
//think of a better name!
Class ClientCreator
{
public function __construct(Request $request, validator $validator){}
public function getClient(){}
public function isValid(){}
public function getErrors(){}
}
Our method now becomes:
public function addClientAction(ClientCreator $creator)
{
if($creator->isValid()){
$this->clientRepository->add($creator->getClient());
}else{
//handle errors
}
}
There is no hard and fast rule as to what number of dependencies are too many.
The good news is if you have built your app using loose-coupling, re-factoring is relatively simple.
I would much much rather see a constructor with 6 or 7 dependencies than a parameterless one and a bunch of static calls hidden throughout the methods
One issue with facades is that additional code has to be written to support them when doing automated unit testing.
As for solutions:
1. Resolving dependencies manually
One way of resolving dependencies, if you do not wish to do it via. constructors or methods injection, is to call app() directly:
/* #var $email_services App\Contracts\EmailServicesContract
$email_services = app('App\Contracts\EmailServicesContract');
2. Refactoring
Sometimes when I find myself passing too many services, or dependencies into a class, maybe I have violated the Single Responsibility Principe. In those cases, maybe a re-design is needed, by breaking the service or dependency into smaller classes. I would use another service to wrap up a related group of classes to serve something as a facade. In essence, it'll be a hierarchy of services/logic classes.
Example: I have a service that generate recommended products and send it out to users via email. I call the service WeeklyRecommendationServices, and it takes in 2 other services as dependency - a Recommendation services which is a black-box for generating the recommendations (and it has its own dependencies -- perhaps a repo for products, a helper or two), and an EmailService which maybe has Mailchimp as a dependency). Some lower-level dependencies, such as redirects, validators, etc. will be in those child services instead of the service that acts as the entry point.
3. Use Laravel global functions
Some of the Facades are available as function calls in Laravel 5. For instance, you can use redirect()->back() instead of Redirect::back(), as well as view('some_blade) instead of View::make('some_blade'). I believe it's the same for dispatch and some other commonly used facades.
(Edited to Add) 4. Using traits
As I was working on queued jobs today, I also observe that another way to inject dependencies is by using traits. For instance, the DispathcesJobs trait in Laravel has the following lines:
protected function dispatch($job)
{
return app('Illuminate\Contracts\Bus\Dispatcher')->dispatch($job);
}
Any class that uses the traits will have access to the protected method, and access to the dependency. It's neater than having many dependencies in the constructor or method signatures, is clearer (about what dependencies are involved) than globals and easier to customize than manual DI container calls. The drawback is that each time you invoke the function you have to retrieve the dependency from the DI container,
Class methods that form a part of the routing mechanism in Laravel (middleware, controllers, etc.) also have their type-hints used to inject dependencies - they don't all need to be injected in the constructor. This may help to keep your constructor slim, even though I'm not familiar with any four parameter limit rule of thumb; PSR-2 allows for the method definition to be stretched over multiple lines presumably because it's not uncommon to require more than four parameters.
In your example you could inject the Request and Validator services in the constructor as a compromise, since they're often used by more than one method.
As for establishing a consensus - Laravel would have to be more opinionated for applications to be similar enough to utilise a one-size-fits-all approach. An easier call though is that I think facades will go the way of the dodo in a future version.
Not so much an answer but some food for thought after talking to my colleagues who have made some very valid points;
If the internal structure of laravel is changed between versions (which has happened in the past apparently), injecting the resolved facade class paths would break everything on an upgrade - while using the default facades and helper methods mostly (if not completely) avoids this issue.
Although decoupling code is generally a good thing, the overhead of injecting these resolved facade class paths makes classes cluttered - For developers taking over the project, more time is spent trying to follow the code which could be spent better on fixing bugs or testing. New developers have to remember which injected classes are a developers and which are laravels. Developers unfamiliar with laravel under the hood have to spend time looking up the API. Ultimately the likelihood of introducing bugs or missing key functionality increases.
Development is slowed and testability isn't really improved since facades are already testable. Rapid development is a strong-point of using laravel in the first place. Time is always a constraint.
Most of the other projects use laravel facades. Most people with experience using laravel use facades. Creating a project that doesn't follow the existing trends of previous projects slows things down in general. Future inexperienced (or lazy!) developers may ignore facade injection and the project may end up with a mixed format. (Even code reviewers are human)
Well your thoughts and concerns and correct and I had them as well.
There are some benefits of Facades ( I generally dont use them ), but if you do use just I would suggest using them only in the controllers, as the controllers are just entry and exit points for me at least.
For the example you gave I'll show how I generally handle it:
// MyServiceInterface is binded using the laravel container
use Interfaces\MyServiceInterface;
use Illuminate\Http\Request;
use Illuminate\Validation\Factory as Validator;
...
class ExampleController {
protected $request;
public function __constructor(Request $request) {
// Do this if all/most your methods need the Request
$this->request = $request;
}
public function exampleController(MyServiceInterface $my_service, Validator $validator, $user_id)
{
// I do my validation inside the service I use,
// the controller for me is just a funnel for sending the data
// and returning response
//now I call the service, that handle the "business"
//he makes validation and fails if data is not valid
//or continues to return the result
try {
$viewinfo = $my_service->getViewInfo($user_id);
return view("some_view", ['view_info'=>$viewinfo]);
} catch (ValidationException $ex) {
return view("another_view", ['view_info'=>$viewinfo]);
}
}
}
class MyService implements MyServiceInterface {
protected $validator;
public function __constructor(Validator $validator) {
$this->validator = $validator;
}
public function getViewInfo($user_id, $data)
{
$this->validator->validate($data, $rules);
if ($this->validator->fails()) {
//this is not the exact syntax, but the idea is to throw an exception
//with the errors inside
throw new ValidationException($this->validator);
}
echo "doing stuff here with $data";
return "magic";
}
}
Just remember to break your code to small individual pieces that each one handles his own responsibility.
When you properly break your code, in most cases you will not have so many constructor parameters, and code will be easily testable and mocked.
Just one last note, if you are building a small application or even a page in a huge application for example a "contact page" and "contact page submit", you can surely do everything in the controller with facades, it simply depends on the complexity of the project.
I love the laravel due to its beautiful architecture.Now as from my approach i wouldnt inject all the facades in to the controller method only why? Injecting Redirect facades only in controller wrong practices as it might need in other. And mainly the things that are mostly used should be declared for all while for those who uses some or only then its best practice to inject them via method as when you declare at top it will hamper in your memory optimization as well as the speed of your code. Hope this would help
Note: as of version 2.8, Symfony provided autowire: true for service configuration, and as of version 3.3, Symfony provided alias (instead of autowire_types) to alias a concrete object to an interface for automatic dependency injection into 'controllers as services'. There's also a bundle to allow autowiring for controller 'action' methods, although I've moved away from this and have focussed more on a variation of the ADR pattern (which is, basically, a single 'action' class with an interface method and not shoving a load of actions methods within a single class which eventually makes for an architectural nightmare). This is, effectively, what I've been looking for all these years and now no longer need to 'hook-in' a decent recursive dependency injector (auryn) as the framework now handles what it should have four years previous. I'll leave this answer here in case anyone wants to trace the steps that I did to see how the kernel works and some of the options available at this level.
Note: Although this question primarily targets Symfony 3, it should also be relevant to users of Symfony 2 as the kernel logic doesn't seem to have changed much.
I want to change how controllers are instantiated in Symfony. The logic for their instantiation currently resides in HttpKernel::handle and, more specifically, HttpKernel::handleRaw. I want to replace call_user_func_array($controller, $arguments) with my own injector performing that specific line instead.
The options I have tried thus far:
Extending HttpKernel::handle with my own method and then having this called by symfony
http_kernel:
class: AppBundle\HttpKernel
arguments: ['#event_dispatcher', '#controller_resolver', '#request_stack']
The downside of this is that, because handleRaw is private, I can't extend it without hacky reflection and so I would have to copy and paste a tonne of code.
Creating and registering a new controller resolver
controller_resolver:
class: AppBundle\ControllerResolver
arguments: []
This was a fundamental misunderstanding I had so I thought I'd document it here. The resolver's job is to resolve where to find the controller as a callable. It hasn't actually been called yet. I am more than happy with how Symfony takes the routes from routes.yml and figures out the class and method to call for the controller as a callable.
Adding an event listener on kernel.request
kernel.request:
class: MyCustomRequestListener
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: 33 /** Important, we'll get to why in a minute **/ }
Taking a look at the Http Kernel Component Documentation, we can see that it has the following typical purpose:
To add more information to the Request, initialise parts of the system, or return a Response if possible (e.g. a security layer that denies access).
I figured that by creating a new listener, using my custom injector to create my controller, and then return a response in that listener, would bypass the rest of the code that instantiates the controller. This is what I want! But there's a major flaw with this:
The Symfony Profiler doesn't show up or any of that stuff, it's just my response and that's it. Dead. I found that I can switch the priority from 31 to 33 and have it switch between my code and Symfonys, and I believe this is because of the router listener priority. I feel I'm going down the wrong path here.
Listening on the kernel.controller event.
No, this allows me to change the callable that will be called by call_user_func_array(), not how the controller is actually instantiated, which is my goal.
I've documented my ideas but I'm out. How can I achieve the following?
Change how the controllers are instantiated and then executed, specifically call_user_func_array() which is in a bloody private method (thanks Symfony)
Fall back to the default controller instantiation if mine doesn't work
Allow everything else to work as expected, like the profiler loading
Be able to bundle this up with an extension for other users
Why do I want to do this?
Controllers can have many different methods for different circumstances and each method should be able to typehint for what it individually requires rather than having a constructor take all the things, some of which may not even be used depending on the controller method being executed. Controllers don't really adhere to the Single Responsibility Principle, and they're an 'object edge case'. But they are what they are.
I want to replace how controllers are created with my own recursively autowiring injector, and also how they are executed, again with recursive introspection via my injector, as the default Symfony package does not seem to have this functionality. Even with the latest "autowire" service option in Symfony 2.8+.
The controller resolver actually does two things. The first is to get the controller. The second is to get a list of arguments for a given action.
$arguments = $this->resolver->getArguments($request, $controller);
$response = call_user_func_array($controller, $arguments);
It is the getArguments method that you could override to implement your special "action method injection" functionality. You just need to determine what arguments the action method needs and return an array of them.
Based on a different question, I also think you might be misunderstanding the autowire functionality. Autowire really only applies to constructor injection. It's not going to help with action method injection.
If the getArguments does not solve your requirement then overriding the handle method is really your only option. Yes there is quite a bit of code to copy/paste from handleRaw but that is because there is quite a bit to do in there. And even if handleRaw was protected you would still have to copy/paste the code just to get at the one line you want to replace.
Why don't you return your own callable from custom ControllerResolverInterface that would instantiate Controller in a way you want and call it?
It would be basically a decorator.
You can extend Symfony\Component\HttpKernel\Controller\ControllerResolver with your own implementation of instantiateController() method, or you can implement ControllerResolverInterface from the scratch.
UPD:
When Symfony makes a call_user_func_array($controller, $arguments); call in handleRaw(), the $controller variable is what you've returned from your custom ControllerResolver. That means you can return any callable from your resolver (it can be [$this, "callController"] f.e.) and inside this callable you would create a new Controller with Auryn and call it.
UPD2:
If you're still struggling with this, I'll add an example because you might miss what I meant here.
use Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver;
class AutowiringControllerResolver extends ControllerResolver
{
// ... constructor stuff; assume $injector is a part of this class
protected function createController($controller)
{
$controller = parent::createController($controller);
return function (...$arguments) use ($controller) {
// you can do with resolved $arguments whatever you want
// or you can override getArguments() method and return
// empty array to discard getArguments() functionality completely
return $this->injector->execute($controller);
};
}
protected function instantiateController($classname)
{
return $this->injector->make($classname);
}
}
Listener is too late to solve your needs, since it excludes Dependency Injection container, which is crucial to create a valid object (~= service).
You are probably looking for Controller Autowiring feature.
If that's so, you might find solution or at least inspiration in this bundle: http://www.tomasvotruba.cz/blog/2016/03/10/autowired-controllers-as-services-for-lazy-people/
It meets your needs in these points:
autowire injector
fallback to default (FrameworkBundle's) controller resolver, if not found
it also should keep all the flow working, since there are no hacks during controller resolving process
I'm making my own primitive MVC framework with PHP, and I'm wondering where I should load/instantiate corresponding controller dependencies?
In the constructor of each controller (tightly coupled) or inject them (loosely coupled)?
The only part of the latter that I'm not too sure of is for the dependencies to be instantiated on bootstrap level, outside of the MVC paradigm, before being injected. Not every controller uses the exact same dependencies besides the default parent ones. I would have to instantiate them all, which would also create a lot of overhead.
I've seen some existing frameworks do it like $this->load->model('model'); // CodeIgniter in the constructor, but I have no clue on why they're doing it like that.
I would suggest you inject the dependencies, so your controllers are less coupled to your framework. This will make a switch to another framework easier.
About instantiating dependencies: I suggest you use (or implement) a dependency injection container. This container should contain factories that can instantiate services.
In an ideal situation your controllers are services too (meaning they too have factories in the dependency injection container).
This way only the controller you need for a particular request will be instantiated, and therefor only its dependencies are instantiated.
When building you own framework, this means that after the routing phase (when the correct controller is known), the framework should grab that controller from the container. The container itself will make sure all dependencies that are needed will be provided.
Have a look at Pimple for an example of a simple dependency injection container.
PS: That line from CodeIgniter looks a lot like the service locator pattern. This pattern is similar to dependency injection, but does not provide full inversion of control.
Q: Where should i load/instantiate corresponding controller dependencies?
There are multiple ways.
The load and instantiation concepts are basically "before/outside" and "after/inside".
Before and outside means, that you load the file containing a class (which you want to instantiate and pass to the controller), before you load the controller.
But how do you know, what the controller needs, before loading the controller? Uh..
Dependency Description Files
A description file comes into play, describing the wiring between your controller and it's dependencies. In other words, you can see the dependencies of your controller by looking at it's dependency description file. This concept is often used by Dependency Injection tools, which analyze the object and pull the dependencies names out automatically. It's also possible to maintain such a wiring configuration file manually. But it's tedious.
Service Locator
A Service Locator is a instantiation helper for dependencies.
Basically, it contains the same information like a dependency description file, but this time in form of a registry. The link between parts of your application becomes this registry.
Both strategies introduce overhead. It's a trade-off. When you change the perspective and look at things from an application with maybe 500+ classes, then you realize that a dependency injection tool is sometimes worth it.
Manual Injection
via Constructor Injection.
After and inside means, that you load the file containing your controller and then start to care about the dependencies.
At this point the class is not instantiated, yet, but the autoloader might do it's dirty deeds behind the scene. He evaluates the use statements at the top of your controller file. The use statements declare namespaced classes, which the autoloader resolves to actuall files and loads them. You might then start to use these classes as dependencies in your controller. This is probably the easiest way to solve your problem and i strongly suggest looking into the topics autoloading with namespaces and use-statements.
When the class is instantiated, you have the following possiblities:
use might use Setter Injection or Reference Injection to set the dependencies to the object. This requires that your Constructor Dependencies are already solved or your constructor is empty.
It's possible to combine these strategies.
Q: What does this do $this->load->model('model'); // CodeIgniter?
CodeIgniter is a legacy application framework. It was created in times, when namespaced autoloading wasn't available. $this->load is a basic class loading helper. This is the opposite of an "auto"loader, (which surprise, surprise) loads things automatically.
CodeIgniters loader class is used to load various other classes, like libraries or files from the view, helpers, models or user defined stuff. This is again the concept of a registry. Here the registry just knowns where things are in your application layout and resolves them. So $this->load->model('model'); means that the modelfunction must have some piecies of information, about the position of model files in your application.
You provide a model name and the path for the file is constructed by model.
And this is exaclty what it does (except a bit of overhead): https://github.com/EllisLab/CodeIgniter/blob/develop/system/core/Loader.php#L223.
Since I'm a Symfony developer, I can only give you a reference to Symfony.
I think you should do like they are doing in Symfony by thinking about what you need in each
Controller object.
At least, you need :
a Request object
and a Model loader object that gives you every Model you need.
Create a BaseController that implements these few functions and then extend it with custom Controllers.
You can also take a look on Silex : http://silex.sensiolabs.org/ a Micro Framework
Hope it helps.
When do you say "In the constructor" you mean to pass in the conatiner and pull the dependencies from them (in the constructor)?
<?php
class SomeController
{
public function __construct($container)
{
$this->service1 = $contanier->get('service1);
}
//...
}
I advice against that, though simpler and easier you will be coupling your controllers to the container thus using a ServiceLocator instead of truly inversion of control.
If you want your controllers to be easy unit-testable you should use inversion of control:
class SomeController
{
public function __construct($service1)
{
$this->service1 = $service1;
}
//...
}
And you can even create your controller as a service inside the container:
// this uses Pimple notation, I hope you get the point
$container['controller'] = function($c) {
return SomeController($c['service1']);
}
Use proxy services to lazy load them
Also if your controllers needs more than some services and you won't be using all of them you can:
1) Use proxy services in order to lazy load the service only when they are really needed
<?php
class ProxyService
{
/**
* #var Service1Type
*/
private $actualService;
public function __construct()
{
$this->actualService = null;
}
private function initialize()
{
$this->actualService = new Service1(); // This operation may take some time thus we deferred as long as possible
}
private function isInitialized()
{
return $this->actualService === null;
}
public function someActionOnThisService()
{
if (!$this->isInitalized()) {
$this->initalize();
}
$this->actualService->someActionOnThisService();
}
There you have a simple proxy object with lazy loading. You may want to check the fantastic Proxy Manager Library if you want to go that route
2) Split your controller
If your contoller has too many dependencies, you may want to split it.
In fact you may want to read the proposal by Paul M. Jones (lead developer of Aura Framework) about MVC-Refinement, IMHO is a good read even though you may not fully agree with it.
Even if you split your controller in order to reduce the dependencies, lazy loading your dependencies is a good idea (obviously you'll have to check weather if its doable in your context: more work in order to gain more speed).
Maybe you need to define __autoload() function before you try to load the Classes which is not loaded yet. Like:
function __autoload($className) {
require "/path/to/the/class/file/$className.php";
}
My example is very very simple to auto require the file which the class definition is in.
You can also use if-else statement or switch statement in that function to fit your own situations smartly.
The __autoload() function is called when PHP doesn't find the class definition, works for new, class_exists(), call_user_method(), etc, and absolutely for your dependences/parents classes. If there is still no class definition after __autoload() is called, PHP will generate an error.
Or you can use spl_autoload_register() function instead of __autoload() more gracefully.
For more information, you might want to see:
http://php.net/manual/en/function.autoload.php
http://php.net/manual/en/function.spl-autoload-register.php