PHP DI container - reading what objects the constructor requests - php

I need to make a DI container which will automatically inject the needed dependencies.
I only have one idea to do this,
Lets say i have initialized a controller using the container, the container will get the namespace of that object and then it will detect what dependencies it requests. The container should get the namespaces of the dependencies that needs to be injected, and then it will create them, then create a new object witg these depndencies and return it.
But the question is, how can you check what dependencies the constructor requests in php?

ReflectionFunction::getParameters() or ReflectionMethod::getParameters() return an array of ReflectionParameter instances.
Now on the ReflectionParameter instances you can use ReflectionParameter::getClass() to get the classes you need.
To get the class' name, you need to access the name property of that ReflectionClass which was returned by ReflectionParameter::getClass().

Related

Pass class by config using ::class and retrieve in Laravel

I have a config file with such array:
'ppr' => [
'validate' => TestRequest::class
];
Now, I want to retrive this class in other part of the system and use it to validate form (outside of the controller).
While using config('main.ppr.validate') all I receive is namespaced name of the class instead of the class object.
As I already accepted that it won't be that easy to just use reuqests as in controllers, I still do wonder how to pass a class by config.
While passing eloquent models it works like a charm (or i.e. config arrays with middlewares etc.), so I suppose there is some magic binding to the IoC to achive that, is it true?
My question is, how to use class passed as in example above without initializing it like:
$obj = new $className;
Laravel (and many other applications) use Dependency Injection to achieve this magic -- your words, not mine! :D
It seems that the Service Container is what handles this in Laravel and should be of help to you.
Directly from the Laravel docs (linked above):
Within a service provider, you always have access to the container via the $this->app property. We can register a binding using the bind method, passing the class or interface name that we wish to register along with a Closure that returns an instance of the class:
$this->app->bind('HelpSpot\API', function ($app) {
return new HelpSpot\API($app->make('HttpClient'));
});
Also:
You may use the make method to resolve a class instance out of the container. The make method accepts the name of the class or interface you wish to resolve:
$api = $this->app->make('HelpSpot\API');
And:
If some of your class' dependencies are not resolvable via the container, you may inject them by passing them as an associative array into the makeWith method:
$api = $this->app->makeWith('HelpSpot\API', ['id' => 1]);
IMHO, I would look up where/how this is implemented in the native Laravel code (usually the Illuminate vendor) and see how it is used / meant to be implemented.
Furthermore, ClassName::class will return the namespace + class of that class. This is why you only see the class name and are not actually receiving an object/instance of that class.
I'm not sure what/where/why/how you're implementing your class and why you need this functionality somewhere that it doesn't already exist. Laravel is pretty good about already having things set up where you need them, so think twice before breaking out of the box and make sure there isn't a default solution for your situation!

What is the difference between app::make and use

Given I want to initialize a class.
I can do
$testClass = App::make('TestClass') //of course given its already binded on container
and
$testClass = new TestClass;
What is the difference, does App::make() when its called resolves the container all the time or ?
Yes, App::make resolves the class using Laravel container every time. Using the containter is a great idea since you can create an advanced binding, bind a class to an interface, bind a class as a singleton etc.
new Class syntax will create a new instance of the Class.
App:make() resolves all class constructor dependencies from application container or uses autowiring if dependency is not registered in container.

Correct implementation of Factory pattern in PHP

I have a question about correct way of implementing factory pattern in PHP used for instantiating complex objects with their dependencies. Let's assume the factory class has a 'build' method, so that for example class User is created in following way:
$factory->build('User');
The class User needs to retrieve the user data from the repository (database), so it depends on Repository object - the constructor looks like:
public function __construct(Repository $repository);
This means that 'build' method of the factory needs to call something like this
$user = new User(new Repository());
Now let's assume I also need to instantiate HomeController object which displays content on the home page. It is retrieving a list of latest articles - to display them on the home page, so it needs repository object as well. So build method will call something like this:
$home = new HomeController(new Repository());
So it is clear now that we have two instances of Repository object, while in fact one instance would be probably enough in this case. So I was wondering if it is a good practise for factory pattern to actually register instantiated Repository object (store it in $registeredObjects array of Factory object) and return it from the table if it was instantiated before. So the object creation in build method would look then like so:
$user = new User($this->build('Repository')); // Here Repository is created first time.
$home = new HomeController($this->build('Repository')); //Here it is retrieved from list of already registered objects
Repository object act in fact as a singleton in this case. I am wondering whether this approach is correct or it is better to instantiate two independent Repository objects.

PhalconPHP DI : Initializers

I am currently testing phalcon php for a project, and I am looking for a way to automatically inject certain classes automatically based on an implemented interface.
The Dependency Injection reference has an example where if a class implements Phalcon\DI\InjectionAwareInterface, it will automatically inject the DI into that class.
What I want to do is similar. If a class has for instance Aranea\Db\DbAdapterAware, it should automatically inject the DbAdapter in that class. I am looking for something similar to what Zend Framework 2 does (https://juriansluiman.nl/article/121/interface-injection-with-initializers-in-zend-servicemanager), where during DI config you can specify initializers like this:
'initializers' => array(
'logger' => function($service, $sm) {
if ($service instanceof LoggerAwareInterface) {
$logger = $sm->get('logger');
$service->setLogger($logger);
}
}
),
If this is not automatically possible in PhalconPHP, I was thinking of overriding the FactoryDefault class and implement it myself. What would be the right place to inject this logic? In the get* methods, or rather in the set* methods? I assume that a method is not initialized during DI initializing but on first call, so get* would sounds more appropriate?
Thanks for your advice,
Jeroen
The Dependency Injection reference has an example where if a class implements Phalcon\DI\InjectionAwareInterface, it will automatically inject the DI into that class.
That is not entirely true, what it means is that the DI gets (automatically) injected when the service is resolved given it implements this interface, the DI doesn't magically appears there just because the class implements some interface.
If a class has for instance Aranea\Db\DbAdapterAware, it should automatically inject the DbAdapter in that class.
That is sort of how it works (not technically) if your class extends the Phalcon\DI\Injectable (or implements the InjectionAwareInterface in the same way as Phalcon\DI\Injectable). Inside Injectable there is a __get magic, which returns the service from the DI if the service exists. In other words stuff get injected only in the DI, and other classes lookup for services in there.
To inject your own services you can either pass them in your configuration to the DI or extend the DI or FactoryDefault. The difference between the two is that FactoryDefault already comes preconfigured with the useful services, which you might not need though.
I assume that a method is not initialized during DI initializing but on first call, so get* would sounds more appropriate?
Yes, there is a Phalcon\DI\Service object that represents the service and resolved when called for the first time (if it's a shared service) or resolved every time (if it's not). You normally would want all your services to be shared, otherwise this often becomes a bottleneck, e.g., when resolving a non-shared database adapter, which establishes the connection every time you call it…
PS: Note, for it to work as you want it with the DbAdapter you can do a few things:
Add the adapter getter and return DI::getDefault()->getShared('db');
Extend the Phalcon\DI\Injectable and set the DI when the class is created, so it can lookup for services.
Every time you need the adapter simply get it from the DI like shown in the first option.

Symfony2 Passing instantiated Object to Service, rather than new object

im looking to pass an already instatiated object to my service constructer, rather than creating a new one, to create a new one i do this:
services:
ExampleService:
class: Ex\ModelBundle\lib\Ex\ExSave
LoginService:
class: Snap\ModelBundle\Service\Login
arguments: ["#doctrine.orm.entity_manager", "#ExampleService"]
How would i pass an already instatiated ExampleService to LoginService
By default all services have container scope which according to docs mean:
The same instance is used each time you request it from this container.
So, you create your ExampleService, but any other, subsequent, request for that service is going to return its previously created instance.
It all boils down to point that multiple services can (and will) use shared instance of your ExampleService.
Alternately, you could use Setter Injection.
Basically, you don't pass #ExampleService to __construct but rather to separate setter which will set the property value (within the service). Then you use your service as usual.

Categories